This file will take you through recreating the figures presented in the manuscript. Note that some of the figures went through additional edits in Illustrator for additional labelling!

Preparation

Load packages

library(dplyr)
Warning: package ‘dplyr’ was built under R version 4.2.3

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
Warning: package ‘ggplot2’ was built under R version 4.2.3
library(forcats)
library(here)
here() starts at /Users/avrilwang/Desktop/Project-Plasmodium
library(deSolve)
Warning: package ‘deSolve’ was built under R version 4.2.3
library(crone)
library(optimParallel)
Loading required package: parallel
library(doParallel)
Loading required package: foreach
Loading required package: iterators
library(doRNG)
Loading required package: rngtools
library(arrow)
Some features are not enabled in this build of Arrow. Run `arrow_info()` for more information.
The repository you retrieved Arrow from did not include all of Arrow's features.
You can install a fully-featured version by running:
`install.packages('arrow', repos = 'https://apache.r-universe.dev')`.

Attaching package: ‘arrow’

The following object is masked from ‘package:utils’:

    timestamp
library(stringr)
Warning: package ‘stringr’ was built under R version 4.2.3
library(parallel)
library(ggpubr)
library(scales)
Warning: package ‘scales’ was built under R version 4.2.3
library(bayestestR)
library(purrr)

Attaching package: ‘purrr’

The following object is masked from ‘package:scales’:

    discard

The following objects are masked from ‘package:foreach’:

    accumulate, when
library(tidyr)
library(splines2)
library(splancs)
Loading required package: sp

Spatial Point Pattern Analysis Code in S-Plus
 
 Version 2 - Spatial and Space-Time analysis


Attaching package: ‘splancs’

The following object is masked from ‘package:tidyr’:

    tribble

The following object is masked from ‘package:dplyr’:

    tribble

load variables

parameters_tsukushi <- c(R1 = 8.89*10^6, 
                lambda = 3.7*10^5,
                mu = 0.025, 
                p = 8*10^-6, 
                alpha = 1, 
                alphag = 2, 
                beta = 5.721, 
                mum = 48, 
                mug = 4, 
                I0 = 43.85965, 
                Ig0 = 0, 
                a = 150, 
                b = 100, 
                sp = 1,
                psin = 16.69234,
                psiw = 0.8431785,
                phin = 0.03520591, 
                phiw = 550.842,
                iota = 2.18*(10^6),
                rho = 0.2627156)

# import in data files
ez_label <- read.csv(here("code_repository/data/ez_label.csv")) ## labelling schene
si_opt.df <- read.csv(here("code_repository/data/si_opt.csv")) ## optimized parameter + fitness list
si_dyn.df <- read_parquet(here("code_repository/data/si_dyn.parquet")) ## dynamics of single cue models
si_rn.df <-  read_parquet(here("code_repository/data/si_rn.parquet")) ## reaction norms of single cue models
si_rug.df <- read_parquet(here("code_repository/data/si_rug.parquet")) ## data for rug plots
mc_all_fitness.df <- read_parquet(here("code_repository/data/mc_all_fitness.parquet")) ## fitness values for mc when all parameters are varying
mc_single_fitness.df <- read_parquet(here("code_repository/data/mc_single_fitness.parquet")) ## fitness values for mc when only one parameter is varying
dual_cue_f_lc.df <- read.csv(here("code_repository/data/dual_cue_fitness_local.csv")) ## optimized dual cue strategy using L-BFGS-B
dual_cue_f_glb.df <- read.csv(here("code_repository/data/dual_cue_fitness_global.csv")) ## optimized dual cue strategy using DEoptim + L-BFG
dual_cue_f_final.df <- read.csv(here("code_repository/data/dual_cue_fitness_final.csv")) ## final dataframe containing strategy that produced highest fitness (dual cue models)
dual_selected_cr.df <- read_parquet(here("code_repository/data/dual_selected_cr.parquet")) ## cr dynamics of selected models
cue_range_si_alt.df <- read.csv(here("code_repository/data/cue_range_si_alt.csv")) ## cue ranges for dual cue models
dual_cue_dyn.df <- read_parquet(here("code_repository/data/dual_cue_dyn.parquet")) ## dynamics of dual cue models
exp_ss.df <- read.csv(here("code_repository/data/experimental_data.csv")) ## cleaned experimental records of P. chabaudi infection
posterior.df <- read.csv(here("code_repository/data/posterior.csv")) ## posterior distribution of different parameter values
si_dyn_30.df <- read_parquet(here("code_repository/data/si_dyn_30.parquet")) ## single cue dynamics data (30 days)
dual_cue_dyn_30.df <- read_parquet(here("code_repository/data/dual_cue_dyn_30.parquet")) ## dual cue dynamics (30 days)
mc_single_dyn_cr_join.df_pr <- read_parquet(here("code_repository/data/mc_sinle_dyn_cr_join.parquet")) ## mc dynamics (single parameter variation) with only cr along with determinstic model cr
mc_single_dyn_cr.sumre <- read.csv(here("code_repository/data/mc_single_dyn_cr_sum.csv")) ## mc dynamics (single parameter) cr summary

## validation data (comparing fitness to random spline)
validation.ls <- list.files(here("code_repository/data/si_validation/"), full.names = T)
validation.df <- do.call(rbind, lapply(validation.ls, read.csv))

# import in code
source(here("code_repository/functions/chabaudi_si_clean.R"))
source(here("code_repository/functions/par_to_df.R"))
source(here("code_repository/functions/chabaudi_si_clean_high.R"))
source(here("code_repository/functions/par_to_hm.R"))
source(here("code_repository/functions/par_to_hm_te.R"))
source(here("code_repository/functions/chabaudi_si_static.R"))

# color codes
orange <- "#fc8d59"
blue <- "#4575b4"

#=================================# # Cue perception mediates fitness #=================================# #———— A. conversion rate dynamics and fitness values ————# ## function for dynamics generation

# function for getting single cue infection dynamics
get_si_dyn <- function(df){
  ## processing model input
  par <- c(df$var1, df$var2, df$var3, df$var4) ## parameters
  cue <- df$cue ## cue choice
  log <- ifelse(df$log=="log", "log10", "none") ## log or not
  cue_range <- seq(df$low, df$high, by = df$by) ## cue range
  id <- df$id ## id
  
  ## get dynamics data
  dyn <- chabaudi_si_clean(
            parameters_cr = par, 
            parameters = parameters_tsukushi, 
            time_range = seq(0, 20, 0.001), 
            cue = cue, 
            cue_range = cue_range, 
            log_cue = log,
            immunity = "tsukushi",
            solver = "vode",
            dyn = TRUE)
  
  # append id
  dyn2 <- cbind(dyn, id = rep(id, nrow(dyn)))
  
  # return results
 return(dyn2)
}

run function to generate data

## split si_opt into list
si_opt.ls <- split(si_opt.df, seq(nrow(si_opt.df)))

## get dynamics
si_dyn <- mclapply(si_opt.ls, get_si_dyn, mc.cores = 8)

## combine the dynamics file
si_dyn.df <- do.call(rbind, si_dyn)

## save
# write_parquet(si_dyn.df, here("code_repository/data/si_dyn.parquet"))

get 30 day simulation data (not used for fitness but for plotting purposes)

## get dynamics
si_opt.ls2 <- split(si_opt.df %>% filter(cue != "t"), seq(nrow(si_opt.df%>% filter(cue != "t"))))
si_dyn_30 <- mclapply(si_opt.ls2, get_si_dyn, mc.cores = 8)

## combine the dynamics file
si_dyn_30.df <- do.call(rbind, si_dyn_30)
si_dyn_30.df %>% group_by(id) %>% summarise(max_time = max(time))

## save
write_parquet(si_dyn_30.df, here("code_repository/data/si_dyn_30.parquet"))

process data to isolate conversion rate and pair them with fitness

## keep only conversion rate from dynamics
si_dyn_cr.df <- si_dyn.df %>% filter(variable == "cr")

## left join with fitness values
si_dyn_cr_fitness.df <- si_dyn_cr.df %>% 
  left_join(si_opt.df, by = "id") 

## normalize fitness by dividing all values by the fitness values we get when cue = time
si_opt.df <- si_opt.df %>% 
  mutate(fitness_norm = fitness_20 / si_opt.df[si_opt.df$cue == "t",]$fitness_20)

plot

## fitness values ranked from high to low
fitness_rank.plt <- ggplot(si_opt.df, aes(x = fitness_norm, y = fct_reorder(long_label, fitness_20))) +
  geom_bar(stat = "identity", fill = "black") +
  labs(x = "Normalized fitness", y = "Cue") +
  theme_classic() +
  theme(plot.margin = margin(5.5, 0, 5.5, 5.5, "pt"))

## conversion rate dynamic ranked in the same order
fitness_cr.plt <- ggplot(si_dyn_cr_fitness.df, aes(x = time, y = fct_reorder(long_label, fitness_20), fill = value)) +
  geom_raster() +
  scale_fill_viridis_c() +
  labs(fill = "Transmission\ninvestment", x = "Time (days)") +
  xlim(1, 20) + # note that cr at day 0->1 is always 0 due to how the model is set up!
  theme_classic() +
  theme(axis.title.y=element_blank(),
        axis.text.y=element_blank(),  #remove y axis labels
        axis.ticks.y=element_blank()  #remove y axis ticks
        )

## plot together
fitness_rank_cr.plt <- ggarrange(fitness_rank.plt, fitness_cr.plt, widths = c(1.2, 1), align = "h", common.legend = T,
                                 legend = "bottom")
Warning: Removed 11022 rows containing missing values or values outside the scale range
(`geom_raster()`).
Warning: Removed 11022 rows containing missing values or values outside the scale range
(`geom_raster()`).

#———— B. Reaction norms ————# ## function to obtain the cue values sensed by parasites from dynamics datas

get_rug <- function(df){
  ## process cue
  cue <- unique(df$cue)
  
  ## if cue contains "+", we need to first split them up and add them into the final dataframe
   if(stringr::str_detect(cue, "\\+")){
    cue_split <- stringr::str_split(string = cue, pattern = "\\+", simplify = T)
    ## get the two cues 
    cue_temp_1 <- cue_split[[1]]
    cue_temp_2 <- cue_split[[2]]
    ## filter dyn
    rug <- df %>% 
      filter(variable == cue_temp_1 | variable == cue_temp_2) %>% 
      dplyr::group_by(time) %>% 
      dplyr::mutate(sum = sum(value, na.rm = T)) %>% 
      select(time, value = sum, id)
  }
  
  # for cue with no addition, it is simply filtering for the values and returning it
  if(stringr::str_detect(cue, "\\+", negate = T)){
    rug <- df %>% 
      dplyr::filter(variable == cue) %>% 
      dplyr::select(time, value, id)}
  
  return(rug)
}

run function to obtain cue values

# join dynamics data with cue information
si_dyn_cue.df <- left_join(si_dyn.df, si_opt.df, by = "id")

# split based on individual labels
si_dyn_cue.ls <- si_dyn_cue.df %>% group_split(id)

# run function to get rug
si_rug <- mclapply(si_dyn_cue.ls, get_rug, mc.cores = 6)

# combine and save
si_rug.df <- do.call(rbind, si_rug)
write_parquet(si_rug.df, here("code_repository/data/si_rug.parquet"))

function to obtain reaction norm

get_si_rn <- function(df){
  ## read in the parameter sets
  par <- c(df$var1, df$var2, df$var3, df$var4)
  ## get cue range
  cue_range <- seq(df$low, df$high, by = df$by)
  ## nested function to convert parameter set into basis spline function
  rn <- par_to_df(par = par, cue_range = cue_range)
  
  # if parasite is sensing logged cue, we have to exponentiate it back so they are on the same scale! 
  rn2 <- rn
  if(stringr::str_detect(df$log, "log")){rn2$cue_range <- 10^(rn2$cue_range)}

  # append label to reaction norm, dyn, and rug
  rn2 <- data.frame(rn2, id = df$id)
  
  return(rn2)
}

run function to get reaction norms

# split dataframe of optimized rcues
si_opt.ls <- split(si_opt.df, seq(nrow(si_opt.df)))

# run function
si_rn <- lapply(si_opt.ls, get_si_rn)

# bind together
si_rn.df <- do.call(rbind, si_rn)
write_parquet(si_rn.df, here("code_repository/data/si_rn.parquet"))

preparing the dataset for plotting reaction norms and rugs

## we are only plotting relevent ranges of the reaction norm, meaning that these are the cue values actually "sensed" by the parasites in an infection. We can use the rug dataframe to get a rough estimate of that
si_rug_lim.df <- si_rug.df %>% 
  group_by(id)%>% 
  summarise(min_temp = min(value, na.rm = T)*0.9,
         max_temp = max(value, na.rm = T)*1.1) %>% ## calculate min and max for each cue
  left_join(si_opt.df, by = "id") %>% ## join with si_opt so we can get the cmmon cue
  ungroup() %>% 
  group_by(cue) %>% 
  summarise(min = min(min_temp), max = max(max_temp)) 

## process reaction norm data to restrict range and get label
si_rn.df_p <- si_rn.df %>% 
  left_join(select(si_opt.df, id, cue, log), by = "id") %>% ## get cue and log status from si_opt.df
  left_join(si_rug_lim.df, by = "cue") %>% ## via cue, get ranges we want to limit the rn to
  filter(cue_range <= max & cue_range >= min) %>% ## keep only cue values within range
  left_join(select(ez_label, id, cue_label), by = "id") # get labels


## refactor to reorder the order at which the cues are presented
si_rn.df_p$cue_label <- factor(si_rn.df_p$cue_label, 
                              levels = c("Asexual iRBC", "Sexual iRBC", "Total iRBC", "Gametocyte", "RBC"))

## split rug labels by none logged and logged
si_rug_cue.df <- si_rug.df %>% 
  distinct(id, value) %>% ## cut down on number of datapoints. For each id, keep only distinct points
  left_join(select(si_opt.df, id, cue, log), by = "id") %>% ## get cue and log status from si_opt.df
  left_join(select(ez_label, id, cue_label), by = "id") ## get cue label for plotting

## refactor rug
si_rug_cue.df$cue_label <- factor(si_rug_cue.df$cue_label, 
                              levels = c("Asexual iRBC", "Sexual iRBC", "Total iRBC", "Gametocyte", "RBC"))

### split
si_rug_cue.df_none <- si_rug_cue.df %>% filter(log == "none")
si_rug_cue.df_log <- si_rug_cue.df %>% filter(log == "log")

plot reaction norms and associated rug plots

#———— Plotting fitness, dynamics, and rn together ————#

ggarrange(fitness_rank_cr.plt, fitness_rn.plt, widths = c(2.7, 1), align = "h", ncol = 2, labels = c("A", "B"))
Warning: Graphs cannot be horizontally aligned unless the axis parameter is set. Placing graphs unaligned.

#=================================# # Dual cue fitness main figure #=================================# #————Relative ranking of dual cue vs single cue fitness———–# ## Prepare dual cue dataset

## For the dual cue models, we optimized each cue combinations in 2 ways, one with L-BFGS-B with 0.5X9 starting point and another with DEoptim + L-FGBS-B. For each cue combination, we are going to pick the strategy that gave us the higher fitness
dual_cue_f_final.df <- dual_cue_f_glb.df %>% 
  select(id, id_b, label, label_b, fitness_glb = fitness, 
         par1_glb = par1, par2_glb = par2, par3_glb = par3, par4_glb = par4, par5_glb = par5, par6_glb = par6,
         par7_glb = par7, par8_glb = par8, par9_glb = par9) %>% 
  left_join(
    select(dual_cue_f_lc.df,
         id, id_b, fitness_lc = fitness, 
         par1_lc = par1, par2_lc = par2, par3_lc = par3, par4_lc = par4, par5_lc = par5, par6_lc = par6,
         par7_lc = par7, par8_lc = par8, par9_lc = par9), by = c("id", "id_b")
  ) %>% ## rename columns and join global and local optimization fitness dataframes
  mutate(
    fitness = ifelse(fitness_glb > fitness_lc, fitness_glb, fitness_lc),
    par1 = ifelse(fitness_glb > fitness_lc, par1_glb, par1_lc),
    par2 = ifelse(fitness_glb > fitness_lc, par2_glb, par2_lc),
    par3 = ifelse(fitness_glb > fitness_lc, par3_glb, par3_lc),
    par4 = ifelse(fitness_glb > fitness_lc, par4_glb, par4_lc),
    par5 = ifelse(fitness_glb > fitness_lc, par5_glb, par5_lc),
    par6 = ifelse(fitness_glb > fitness_lc, par6_glb, par6_lc),
    par7 = ifelse(fitness_glb > fitness_lc, par7_glb, par7_lc),
    par8 = ifelse(fitness_glb > fitness_lc, par8_glb, par8_lc),
    par9 = ifelse(fitness_glb > fitness_lc, par9_glb, par9_lc)
  ) ## the columns are assigned based on which strategy produced the highest fitness

## write csv
write.csv(dual_cue_f_final.df, here("code_repository/data/dual_cue_fitness_final.csv"))

Combine single cue and dual cue dataset

plot fitness rank

dual_fitness.pl <- ggplot(dual_si_fitness.df) +
  geom_segment(aes(y = fct_reorder(label_comb, fitness_dual_norm), yend = fct_reorder(label_comb, fitness_dual), x = fitness_dual_norm, xend = fitness_si_final)) + ## line segment connecting the cue values
  geom_point(aes(y = fct_reorder(label_comb, fitness_dual), x = fitness_dual_norm, color = "Dual cue", shape = "Dual cue"),
             size = 2.5) + ## dual cue fitness
  geom_point(aes(y = fct_reorder(label_comb, fitness_dual_norm), x = fitness_si_final, color = "Best single cue", shape = "Best single cue"), size = 2.5) + ## best single cue fitness
  scale_color_manual(values=c("#fc8d59", "#4575b4")) +
  labs(x = "Normalized fitness", y = "Dual cue combinations", colour = "Legend", shape = "Legend") +
  theme_classic() +
  theme(legend.position = "top")

#————Time series conversion rate of dual cue models———–# Note that our dual cue models take 9 parameters. To ensure that any difference between dual and single cue models is due to the inclusion of additional cues and NOT higher spline flexibility,we performed L-BFGS-B (local) optimization of single cue models with df = 9 to control for the spline flexibility.

dynamics simulation of high parameter cues

## best dual cue model: I log and R log
Rlog_Ilog.cr <- chabaudi_si_clean(
  parameters_cr = c(4.446192033,    10.97518275,    1.38762817, 23.3059254, -3.452052371,   -18.0070692,    39.66614226,    -3.545193141,   18.78350799),
  immunity = "tsukushi",
  parameters = parameters_tsukushi,
  time_range = seq(0, 20, by = 1e-3),
  cue_range =  seq(6, 7, by = 1/500),
  cue_range_b = seq(0, log10(6*(10^6)), by = (log10(6*(10^6)))/500),
  cue = "R",
  cue_b = "I",
  log_cue = "log10",
  log_cue_b = "log10",
  solver = "vode",
  dyn = T
)

plot

ggplot() +
  geom_line(data = dual_selected_cr.df, aes(color = label_new, x = time, y = value), size = 1) +
  geom_point(data = dual_selected_cr.df %>% filter(time%%1 == 0), aes(color = label_new, x = time, y = value, shape = label_new), size = 3) +
  labs(x = "Time (days)", y = "Conversion rate", color = "Cue(s)", shape = "Cue(s)") +
  xlim(0, 20) +
  scale_color_manual(values = c(orange,"#E1BE6A",blue ,"black")) +
  theme_classic() +
  theme(legend.position="right",
        plot.margin = margin(t = 40, r = 0, b = 0, l = 0, unit = "pt")) +
  guides(color = guide_legend(nrow = 4, byrow = TRUE))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_point()`).

#——– Reaction norm heatmap of R log10 + I log10 ————# # Process data

# make heatmap data.frame
Rlog_Ilog.hm <- par_to_hm_te(par = c(4.446192033,   10.97518275,    1.38762817, 23.3059254, -3.452052371,   -18.0070692,    39.66614226,    -3.545193141,   18.78350799),
             cue_range = seq(6, 7, length.out = 500),
             cue_range_b = seq(0,   6.77815125, length.out = 500))

# process dynamics
Rlog_Ilog.dyn <- Rlog_Ilog.cr %>% 
  tidyr::pivot_wider(names_from = variable, values_from = value) %>% 
  mutate(log_R = log10(R),
         log_I = log10(I))

plot

Rlog_Ilog_rn.pl <- ggplot() +
  geom_raster(data = Rlog_Ilog.hm, aes(x = cue_range_b, y = cue_range, fill = cr)) +
  scale_fill_viridis_c() +
  geom_path(data = Rlog_Ilog.dyn, aes(x = log_I, y = log_R), color = "white", arrow = arrow(angle = 30, length = unit(0.1, "inches"))) +
  geom_point(data = Rlog_Ilog.dyn %>% filter(row_number() %% 1000 == 1 & time <= 20), aes(x = log_I, y = log_R), color = "white") +
  xlim(0.99*min(hablar::s(Rlog_Ilog.dyn$log_I), na.rm = T), 1.01* max(hablar::s(Rlog_Ilog.dyn$log_I), na.rm = T)) +
  ylim(0.99*min(hablar::s(Rlog_Ilog.dyn$log_R), na.rm = T),1.01* max(hablar::s(Rlog_Ilog.dyn$log_R), na.rm = T)) +
  labs(y = "RBC log", x = "Asexual iRBC log", fill = "Transmission\ninvestment") +
  theme_classic() +
  theme(legend.position = "right")

#———– Plot together ————#

# assemble panel B and C
dual_main.BC <- ggarrange(dual_selected_cr.pl, Rlog_Ilog_rn.pl, align = "v", ncol = 1, labels = c("B", "C"))
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_line()`).
Warning: Removed 3 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 219202 rows containing missing values or values outside the scale range
(`geom_raster()`).
# assemble panel A
ggarrange(dual_fitness.pl, dual_main.BC, ncol = 2, labels = c("A", ""), widths = c(1.1,1))
ggsave(here("code_repository/figures/dual_main_intermediate.tiff"), units = "px", width = 2250, height = 1400, scale = 1.3, dpi=300,  bg = "white")

#=================================# # Dual cue conversion rate supplementary figure (cr dynamics) #=================================# ## Function to simulate dual cue dynamics

dual_cue_dyn <- function(df){
  ## process cues
  cue <- df$cue
  cue_b <- df$cue_b
  
  ## process log
  log <- ifelse(str_detect(df$id, "log"), "log10", "none")
  log_b <- ifelse(str_detect(df$id_b, "log"), "log10", "none")
  
  # process cue_range. ensure that both cue ranges are of the same length
  cue_range <- seq(df$low, df$high, length.out = 500)
  cue_range_b <- seq(df$low_b, df$high_b, length.out = 500)
  
  # get parameter set
  par <- c(df$par1, df$par2, df$par3, df$par4, df$par5, df$par6, df$par7, df$par8, df$par9)
  
  # simulate dynamics
  dyn <- chabaudi_si_clean(
    parameters_cr = par,
    immunity = "tsukushi",
    parameters = parameters_tsukushi,
    time_range = seq(0, 20, 0.01),
    cue = cue,
    cue_b = cue_b,
    cue_range = cue_range,
    cue_range_b = cue_range_b,
    log_cue = log,
    log_cue_b = log_b,
    solver = "vode",
    gam = "te",
    dyn = T)
  
  # return 
  dyn2 <- cbind(id = df$id, id_b = df$id_b, 
                label = df$label, label_b = df$label_b,
                cue = cue, cue_b = cue_b, dyn)
  
  write_parquet(dyn2, here(paste0("code_repository/data/dual_cue_dyn/", df$id, "_", df$id_b, ".parquet")))
}

Get dynamics of dual cue models

## For the dual cue fitness dataframe, add the cue range for each individual cue
dual_cue_f_final.df_p <- dual_cue_f_final.df %>% 
  left_join(select(cue_range_si_alt.df, id, cue, low, high), by = "id") %>% 
  left_join(select(cue_range_si_alt.df, id_b = id, cue_b = cue, low_b = low, high_b = high), by = "id_b")

## Split dataframes
dual_cue_f_final.ls <- split(dual_cue_f_final.df_p, seq(nrow(dual_cue_f_final.df_p)))

## Run function
mclapply(dual_cue_f_final.ls, dual_cue_dyn, mc.cores = 6)

## Concat all files
dual_cue_dyn.ls <- list.files(path = here("code_repository/data/dual_cue_dyn"), pattern = "*.parquet", full.names = T)
dual_cue_dyn.df <- do.call(rbind, lapply(dual_cue_dyn.ls, read_parquet))
write_parquet(dual_cue_dyn.df, here("code_repository/data/dual_cue_dyn.parquet"))

get 30 days dynamic (for plotting purposes)

## Run function
mclapply(dual_cue_f_final.ls, dual_cue_dyn, mc.cores = 6)

## Concat all files
dual_cue_dyn_30.ls <- list.files(path = here("code_repository/data/dual_cue_dyn_30"), pattern = "*.parquet", full.names = T)
dual_cue_dyn_30.df <- do.call(rbind, lapply(dual_cue_dyn_30.ls, read_parquet))
write_parquet(dual_cue_dyn_30.df, here("code_repository/data/dual_cue_dyn_30.parquet"))

Preparing dataset for plotting

Plot

ggplot(data = dual_cue_cr.df, aes(x = time, y = fct_reorder(label_comb, fitness), fill = value)) +
  geom_raster() +
  scale_fill_viridis_c() +
  xlim(1, 20) +
  labs(x = "Time (days)", y = "Dual cue combination", fill = "Transmission\ninvestment") +
  theme_classic()
Warning: Removed 4080 rows containing missing values or values outside the scale range (`geom_raster()`).
ggsave(here("code_repository/figures/dual_cue_cr.tiff"), units = "px", width = 2000, height = 1500, dpi=300,  bg = "white")
Warning: Removed 4080 rows containing missing values or values outside the scale range (`geom_raster()`).

#=================================# # Cumulative transmission plot of dual and single cue models #=================================# # get cumulative tau

# process 
I_high.taucum <- I_high.cr %>% filter(variable == "tau_cum") %>% mutate(label_new = "I log (df=9)") %>% select(-variable)

R_high.taucum <- R_high.cr %>% filter(variable == "tau_cum") %>% mutate(label_new = "R log (df=9)") %>% select(-variable)

time_high.taucum <- time_high.cr %>% filter(variable == "tau_cum") %>% mutate(label_new = "Time (df=9)") %>% select(-variable)

Rlog_Ilog.taucum <- Rlog_Ilog.cr %>% filter(variable == "tau_cum") %>% mutate(label_new = "R log & I log\n(df=9)") %>% select(-variable)

plot

#=================================# # Experimental disease maps of P. chabaudi #=================================# ## Import in data

# import in https://academic.oup.com/emph/article/2018/1/127/5045871?login=true
## (2018 published in EMPH)
emph_2018 <- readxl::read_xls(here("code_repository/experimental_data/Huijben_2018_EMPH.xls"), sheet = 1)

# import in https://onlinelibrary.wiley.com/doi/10.1111/j.1558-5646.2010.01068.x
## (2010 published in Evolution)
evo_2010 <- readxl::read_xls(here("code_repository/experimental_data/Huijben_2010_evolution.xls"), sheet = 1)

# import in https://journals.plos.org/plospathogens/article?id=10.1371/journal.ppat.1003578#:~:text=The%20philosophy%20is%20that%20aggressive,longer%20feel%20sick%20%5B13%5D.
## (2013 in PLoS pathogen)
plos_2013_1 <- readxl::read_xlsx(here("code_repository/experimental_data/Huijben_2013_PLoS.xlsx"), sheet = 2)

plos_2013_2 <- readxl::read_xlsx(here("code_repository/experimental_data/Huijben_2013_PLoS.xlsx"), sheet = 3)

# import in https://onlinelibrary.wiley.com/doi/10.1111/j.1420-9101.2011.02369.x
## (2011 in Journal of Evolutionary Biology)
eseb_2011 <- readxl::read_xls(here("code_repository/experimental_data/Huijben_2011_eseb.xls"), sheet = 1)

# import in https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3939351/
## (2011 in Journal of American naturalist). Note private dataset so not provided in the supplementary!
amna_2011 <- readxl::read_xls(here("experimental_data/Pollitt_2011_naturalist.xls"), sheet = 1)

Clean data

## for EMPH 2018 study, include only infection series without drugs were R-inoculum is administered by itself, which includes 6, 7, 8, 9, 10. Box 6 has a starting inoculum number of 10^6, which is most similar to other studies. Filtering between day 3-21 because those are the days where we have single day data.
emph_2018_ss.df <- emph_2018 %>% 
  filter(Box %in% seq(6, 10) &
         dplyr::between(Day, 3, 21)) %>% 
  mutate(dose = case_when(
    Box == 6 ~ 10^6,
    Box == 7 ~ 10^5,
    Box == 8 ~ 10^3,
    Box == 9 | Box == 10 ~ 10^1
  )) %>% 
  mutate(strain = "As6p",
         study = "emph2018",
         study_strain = paste0(strain, study),
         id = paste0(study, strain, Box, Mouse, 1),
         RBC = RBC * (10^6)) %>%
  select(day = Day,
         mouse = Mouse, 
         RBC, 
         asex = Rasex,
         gam = Rgam,
         dose,
         strain,
         study,
         study_strain,
         id)

## for 2011 eseb, only day 3-17 data are analyzed because those are the days where gametocyte data are available
eseb_2011_ss.df <- eseb_2011 %>% 
  filter(Clones == "R" & between(Day, 3, 17) &
           Drugs == "N") %>% 
  mutate(dose = 10^6,
         strain = "As8p",
         study = "eseb2011",
         study_strain = paste0(strain, study),
         RBC = RBC*(10^6),
         id = paste0(study, strain, Box, Mouse, 2)) %>% 
  select(day = Day,
         mouse = Mouse,
         RBC,
         asex = R.asex,
         gam = R.gam,
         dose,
         strain,
         study,
         study_strain,
         id)

## for evolution_2010, single infection data for both resistant and susceptible clones are available without drug treatment
evolution_2010_ss.df <- evo_2010 %>% 
  filter(Clone == "R" | Clone == "S") %>%
  filter(between(Day, 3, 21) &
           Drugs == "nodrugs") %>% 
  mutate(asex = R.asex + S.asex,
         gam = R.gam + S.gam,
         dose = 10^6,
         RBC = RBC*(10^6),
         study = "evol2011",
         strain = ifelse(Clone == "R", "As12", "AJ51"),
         study_strain = paste0(strain, "_", study),
         id = paste0(study, strain, Box, Mouse, 3)) %>% 
  select(day = Day,
         mouse = Mouse,
         RBC,
         asex,
         gam,
         dose,
         strain,
         study,
         study_strain,
         id)

## for amnat 2011, get single infection data. Filter out any mice that have missing data. Set negative asexuasl data ot 0
amna_2011_ss.df <-  amna_2011 %>% 
  filter(treat %in% c("AJ", "AS", "ER", "CR", "CW", "DK")) %>% 
  mutate(asex = tot.para - tot.gcyte,
         gam = tot.gcyte,
         dose = 10^6,
         study = "amna_2011",
         RBC = rbc/(10^6),
         study_strain = paste0(treat, "_", study),
         id = paste0(study, treat, div, mouse, 4)) %>% 
  mutate(asex = ifelse(asex < 0, 0, asex)) # sometimes total parasite is less than gametocyte so need to correct for this

### check for NA by groups
amna_na.id <- amna_2011_ss.df %>% 
  filter_at(vars(asex, gam, RBC), all_vars(is.na(.))) %>% 
  distinct(id) %>% 
  select(id)

amna_2011_ss.df2 <- amna_2011_ss.df %>% 
  filter(!(id %in% amna_na.id$id)) %>% 
  select(day,
         mouse,
         RBC,
         asex,
         gam,
         dose,
         strain = treat,
         study,
         study_strain,
         id)

## rbind
exp_ss.df <- rbind(emph_2018_ss.df, eseb_2011_ss.df, evolution_2010_ss.df, amna_2011_ss.df2)

## write
write.csv(exp_ss.df, here("code_repository/data/experimental_data.csv"))

Prepare dataset for plotting

names(exp_ss.df) <- c("X", "Day", "Mouse", "RBC", "iRBC", "Gametocyte", "Dose", "Strain", "Study", "Study_strain", "id")

## prepare a list of variable combinations we want to plot
exp_var.comb <- tidyr::expand_grid(x = c("RBC", "iRBC", "Gametocyte"),
                   y = c("RBC", "iRBC", "Gametocyte")) %>% ## get all pairwise combinations of variables
  filter(x != y) %>% ## remove incidences where the 2 variables are the same
  mutate(tmp = paste0(pmin(x, y), pmax(x, y))) %>% ## eliminate same variable but different order
  slice_head(n = 1, by = tmp) %>% 
  select(-tmp) 

List-wise plotting

## x and y axis are not logged!
exp_xy.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
  x_col <- .x
  y_col <- .y
  
  ggplot(exp_ss.df, aes_string(x = x_col, y = y_col)) +
        geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
        theme_classic() + 
        scale_color_viridis_c(option = "A", limits = c(3, 21)) +
        labs(color = "Days post-infection")  +
        scale_y_continuous(labels = function(x) format(x, scientific = TRUE)) +
        scale_x_continuous(labels = label_scientific(digits = 1))
}
)

## x-axis is logged
exp_xlogy.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
  x_col <- .x
  y_col <- .y
  
  ggplot(exp_ss.df, aes_string(x = sprintf("log10(%s)", x_col), y = y_col)) +
        geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
        theme_classic() + 
        scale_color_viridis_c(option = "A", limits = c(3, 21)) +
        labs(color = "Days post-infection", x = paste(x_col, "log"))  +
        scale_y_continuous(labels = function(x) format(x, scientific = TRUE))
}
)

## y-axis logged
exp_xylog.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
  x_col <- .x
  y_col <- .y
  
  ggplot(exp_ss.df, aes_string(x = x_col, y = sprintf("log10(%s)", y_col))) +
        geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
        theme_classic() + 
        scale_color_viridis_c(option = "A", limits = c(3, 21)) +
        labs(color = "Days post-infection", y = paste(y_col, "log"))   +
        scale_x_continuous(labels = label_scientific(digits = 1))
}
)

## both x and y axis is logged
exp_xlogylog.pl_ls <- map2(exp_var.comb$x, exp_var.comb$y, ~ {
  x_col <- .x
  y_col <- .y
  
  ggplot(exp_ss.df, aes_string(x = sprintf("log10(%s)", x_col), y = sprintf("log10(%s)", y_col))) +
        geom_path(aes(colour = Day, group = id), arrow = arrow(type = "closed", angle = 10, length = unit(0, "inches"))) +
        theme_classic() + 
        scale_color_viridis_c(option = "A", limits = c(3, 21)) +
        labs(color = "Days post-infection", x = paste(x_col, "log"), y = paste(y_col, "log")) 
}
)

## plot together
ggarrange(plotlist = c(exp_xy.pl_ls, exp_xlogy.pl_ls, exp_xylog.pl_ls, exp_xlogylog.pl_ls), 
          common.legend = T, align = "hv")
ggsave(here("code_repository/figures/exp_disease-curve.tiff"), units = "px", width = 2250, height = 1500, scale = 1.4, dpi=300,  bg = "white")

#=================================# # Simulated disease curve graph #=================================# ## Function to obtain dynamics data based on the dual cue input

get_dual_rn <- function(df){
  ## assign the two cues
  cue <- unique(df$cue)
  cue_b <- unique(df$cue_b)
  
  ## assign log status
  log <- ifelse(str_detect(unique(df$id), "log"), "log", "none")
  log_b <- ifelse(str_detect(unique(df$id_b), "log"), "log", "none")
  
  ## assign which cues are going to be logged
  if(log == "log" & log_b == "none"){logged_cue <- cue}
  if(log == "none" & log_b == "log"){logged_cue <- cue_b}
  if(log == "log" & log_b == "log"){logged_cue <- c(cue, cue_b)}
  if(log == "none" & log_b == "none"){logged_cue <- c()}

  ## keep variables that corresponds to the cue used
  ### for dataframes that does not involve combined variables such as I+Ig
  if(isTRUE(str_detect(cue, "\\+", negate = T)) & isTRUE(str_detect(cue_b, "\\+", negate = T))){
    df_f <- df %>% 
      filter(variable %in% c(cue, cue_b, "cr")) %>% 
      mutate(value = case_when(
        variable %in% logged_cue ~ log10(value),
        TRUE ~ value
      )) %>% ## log transform values only when they match with the logged cue list
      filter(value >= 0) ## filter out values <0, these happen due to stiffness of models but are not relevant
         
  } else{
    ### assign both cues to a list
    cue_ls <- c(cue, cue_b)
    ### pick the cue that has the "+" sign
    combined_cue <- cue_ls[grepl("\\+", cue_ls)]
    non_combined_cue <- cue_ls[!grepl("\\+", cue_ls)] ### this is the none combined cue
    ### unlist the cues
    cue_unlist <- unlist(str_split(combined_cue, "\\+"))
    
    ### get filtered dataset containing only non-combine cue
    df_f1 <- df %>% 
      filter(variable %in% c(non_combined_cue, "cr"))
    
    ### get filtered dataset containing combined cue. These will be summed up and bound back to the previous
    df_f2 <- df %>% 
      filter(variable %in% cue_unlist) %>% ## keep only variables that we will combine
      group_by(time) %>% ## for each time point, group the variables
      mutate(value = sum(value, na.rm = T),
             variable = combined_cue) %>% ## recalculate the value as sum of the values and reassign variable!
      distinct(time, .keep_all = T) ## note that because we are mutating we must dedeuplicate the records
    
    #### combine the two and log transform if necessary
    df_f <- rbind(df_f1, df_f2) %>% 
      mutate(value = ifelse(variable %in% logged_cue, log10(value), value)) %>% ## log transform values only when they match with the logged cue list
      filter(value >= 0) 
  }
  
  ## Convert dataframes wider such that the different variables have their own columns
  df_fp <- df_f %>% 
    mutate(variable_id = ifelse(variable == cue, paste0(variable, "_", log), paste0(variable, "_", log_b))) %>%  ### assign a unique variable id that could later be used to assign labels
    left_join(select(ez_label, id, long_label), by = c("variable_id" = "id")) %>% 
    mutate(long_label = ifelse(variable == "cr", "cr", long_label)) %>% ## manually add cr
    mutate(long_label = gsub(" ", "_", long_label)) %>%  ## convert spaces to _ for plotting
    pivot_wider(names_from = long_label, values_from = value, id_cols = c(time, id, id_b)) %>% 
    filter(time >= 1) %>% ## filter out day 0->1 because all cr = 0 before that
    arrange(time) ## this is needed to prevent geom_path from joining the first and last data point
  
  ## assign NAs (meaning no stuff is produced yet to 0)
  df_fp[is.na(df_fp)] <- 0

  return(df_fp)
}

Run function to get a curated dataset containing only relevant

## split dual dynamics dataframe into list grouped by the dual cues
dual_cue_dyn.ls <- dual_cue_dyn.df %>% group_split(id, id_b)

## run function across list
dual_cue_rn.ls <- mclapply(dual_cue_dyn.ls, get_dual_rn, mc.cores = 6)

## sanity checks that we are actually summing iRBCs. Rings out
max(dual_cue_rn.ls[[2]]$Total_iRBC)
max(dual_cue_rn.ls[[4]]$Asexual_iRBC)
max(dual_cue_rn.ls[[6]]$Sexual_iRBC)

max(dual_cue_rn.ls[[10]]$Total_iRBC)
max(dual_cue_rn.ls[[12]]$Asexual_iRBC)
max(dual_cue_rn.ls[[14]]$Sexual_iRBC)

plot

## list apply all dataframes
cue_cue_rn_pl.ls <- lapply(dual_cue_rn.ls,
       function(x){
          ## get names of columns used in the x and y axis
         axis_cols <- setdiff(names(x), c("time", "id", "id_b", "cr"))
         
         ## ggplot
         ggplot() +
           geom_path(data = x, aes_string(x = axis_cols[[1]], y = axis_cols[[2]], color = "cr"),
                     arrow = arrow(length = unit(c(rep(0, nrow(x) - 2), 0.25), "inches")),
                     size = 1.5) +
           geom_point(data = x %>% filter(time %% 1 == 0), 
                      aes_string(x = axis_cols[[1]], y = axis_cols[[2]]), size = 1.5, shape = 1) +
           theme_classic() +
           scale_color_viridis_c(limits = c(0, 1)) +
           labs(color = "Conversion rate", x = gsub("_", " ", axis_cols[[1]]), y = gsub("_", " ", axis_cols[[2]])) +
           scale_x_continuous(labels = label_scientific(digits = 2)) +
           scale_y_continuous(labels = label_scientific(digits = 2))
       })

## arrange together
cue_cue_rn.pl <- ggarrange(plotlist = cue_cue_rn_pl.ls, ncol = 5, nrow = 8, common.legend = T, align = "hv") 
ggsave(here("code_repository/figures/sim_disease-curve.tiff"), units = "px", width = 2250, height = 2500, scale = 2.2, dpi=300,  bg = "white")

#=================================# # Curating list of selected exo and simulated # disease curves (main figure) #=================================# # plot

# main figure
ggarrange(
   cue_cue_rn_pl.ls[[31]] +
    scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
    scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
    theme(legend.position = "none"), 
          
   exp_xlogylog.pl_ls[[1]] + theme(legend.position = "none"),

   cue_cue_rn_pl.ls[[35]] + 
     scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
     theme(legend.position = "none",
           axis.text.x=element_text(size=6.5)), 
   
   exp_xylog.pl_ls[[1]] + 
     scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
     theme(legend.position = "none",
           axis.text.x=element_text(size=6.5)),
   
   cue_cue_rn_pl.ls[[30]] + 
     scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
     theme(legend.position = "none"), 
   
   exp_xlogy.pl_ls[[1]] + theme(legend.position = "none"), 
   
   cue_cue_rn_pl.ls[[15]] + 
     scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
     theme(legend.position = "none"), 
   
   exp_xlogy.pl_ls[[2]] + theme(legend.position = "none"),
   
   align = "hv", ncol = 4, nrow = 2
)
ggsave(here("code_repository/figures/sim_exp_disease_curve_main.tiff"), units = "px", width = 2250, height = 1000, scale = 1.25, dpi=300,  bg = "white")


# get legend separately
ggarrange(
   cue_cue_rn_pl.ls[[31]] +
    scale_x_continuous(labels = scales::number_format(accuracy = 0.1)) +
    scale_y_continuous(labels = scales::number_format(accuracy = 1)) +
    theme(legend.position = "top"), 
          
   exp_xlogylog.pl_ls[[1]] + theme(legend.position = "top"))
ggsave(here("code_repository/figures/sim_exp_disease_curve_legend.tiff"), units = "px", width = 2250, height = 500, scale = 1.25, dpi=300,  bg = "white")

#=================================# # MC simulation of single cue and dual cue infection #=================================# #———— Impact of all parameter variation on fitness ————# ## Append all fitness data and sanity checks

length(mc_all_dc.ls)
[1] 20089

process data for plotting fitness

plot fitness variation

#———— Impact of individual parameter variation on fitness ————# ## combine all single parameter variation files

## list of file paths linked to the single parameter files
mc_single.ls <- list.files(path = here("code_repository/data/mc_single_fitness"), pattern = "*.csv", full.names = T)

## read and combine
mc_single_fitness.df <- do.call(rbind, mclapply(mc_single.ls, function(x) read.csv(x), mc.cores = 6))

# look at # of reps
mc_single_fitness.df %>% 
  group_by(id) %>% 
  tally()

## write
write_parquet(mc_single_fitness.df, here("code_repository/data/mc_single_fitness.parquet"))

## sanity check. iter = 1 is where all parameters are default. Should produce the same fitness values!
mc_single_fitness.df %>% filter(iter == 1)

process data for plotting

## note that for each iter across the "single" and "all" dataset, the parameter alteration is the same. Thus, for each data point at which all parameter are varied, there is a corresponding datapoint where only one parameter is varied. We can join these 2 dataset by iter and cue and log
mc_single_all_fitness.df <- mc_single_fitness.df %>% 
  left_join(select(mc_all_fitness.df, fitness_all = max_fitness,id, iter), by = c("id", "iter")) %>% 
  filter(iter %in% seq(0,1000,1)) ## only include the top 1000 records

## make the dataframe into a long format such that all fitness variations are in a single column
mc_single_fitness.long <- mc_single_all_fitness.df %>% 
  filter(iter != 1) %>% ## filter out iter = 1, which does not have variation
  left_join(select(mc_all_fitness_ref.df, fitness_ref = max_fitness, id),
            by = c("id")) %>% ## add in the deterministic fitness values
  select(id, max_fitness_rho, max_fitness_beta, max_fitness_psin, max_fitness_psiw, max_fitness_phin, max_fitness_phiw, iter, fitness_all, fitness_ref) %>% # keep only fitness and associated labels
  tidyr::pivot_longer(-c("id", "fitness_all", "iter", "fitness_ref")) %>% ## make long
  mutate(parameter = gsub("max_fitness_|fitness_", "", name)) ## isolate parameter being altered

## calculate degree deviation from deterministic values. note that rel_diff_single ranges from -1 to 1. -1 ->  one variable perturbation is acting in the opposite direction to the overall perturbation caused by randomizing all variables. 0 parameter variation contributes very little, 1 -> one variable contributes a lot
mc_single_fitness.long_p <- mc_single_fitness.long %>% 
  mutate(diff_single = value-fitness_ref, ## pertubation to fitness caused by single parameter variation
         diff_all = fitness_all-fitness_ref, ## pertubation to fintess caused by all parameter varying
         rel_diff_single = diff_single/diff_all ## normalized pertunation to fitness (single parameter)
         ) %>% 
  left_join(ez_label, by = c("id"))

## calculate summary statistics. this includes median, credible interval (contains 89% data points calculated via Highest Density Interval, which is better for skewed data)
mc_single_fitness.long_p %>% filter(id == "R_log-I+Ig_log")

mc_single_fitness.sum <- mc_single_fitness.long_p %>% 
  group_by(id, parameter, short_label) %>% 
  summarise(ci_lower = ci(rel_diff_single, method = "HDI", ci = 0.89)[[2]],
            ci_higher = ci(rel_diff_single, method = "HDI", ci = 0.89)[[3]],
            quantile_low = quantile(rel_diff_single, 0.025),
            quantile_high = quantile(rel_diff_single, 0.975),
            median = median(rel_diff_single),
            mean = mean(rel_diff_single)) %>% 
  mutate(parameter_label = case_when( ## recode parameter values
    parameter == "rho" ~ "RBC replenishment (ρ)",
    parameter == "phin" ~ "Half-life indis (ϕn)",
    parameter == "phiw" ~ "Half-life targeted (ϕw)",
    parameter == "psin" ~ "Activation indis (ψn)",
    parameter == "psiw" ~ "Activation targeted (ψw)",
    parameter == "beta" ~ "Burst size (β)",
  ))
`summarise()` has grouped output by 'id', 'parameter'. You can override using the `.groups`
argument.
## for violin plots, what we can do is plot out only the 89% credible interval so the graph is easier to interpret
mc_single_fitness.long_p_f <- mc_single_fitness.long_p %>% 
  left_join(select(mc_single_fitness.sum, short_label, parameter, ci_lower, ci_higher, parameter_label), by = c("short_label", "parameter")) %>% 
  filter(rel_diff_single >= ci_lower & rel_diff_single < ci_higher)
Adding missing grouping variables: `id`
## arrange ordering of cues and parameters
mc_single_fitness.long_p_f$short_label <- factor(mc_single_fitness.long_p_f$short_label, c(
  "R & I log", "R log & I log",
  "R & Total I log","R log & Total I log",
  "R", "R log",
  "I", "I log",
  "Ig", "Ig log", 
  "Total I", "Total I log",
  "G", "G log"))

mc_single_fitness.long_p_f$parameter_label <- factor(mc_single_fitness.long_p_f$parameter_label,
                                                     c("Burst size (β)",
                                                       "RBC replenishment (ρ)",
                                                       "Half-life indis (ϕn)",
                                                       "Half-life targeted (ϕw)",
                                                       "Activation indis (ψn)",
                                                       "Activation targeted (ψw)"))

plotting 89% credible interval. By visual inspection of the distribution, ci represented the distribution (more honestly) than quantile, even when quantile seems to display larger differences of logging cues.

#——— arrange plots together ———#

ggarrange(mc_all_fitness.pl, mc_partition.pl, align = "h", widths = c(1.3, 2), labels = c("A", "B"))
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning: Graphs cannot be horizontally aligned unless the axis parameter is set. Placing graphs unaligned.

ggarrange(mc_all_fitness.pl, mc_partition.pl, align = "h", widths = c(1.3, 2), labels = c("A", "B"))
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation indis (ψn)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Activation targeted (ψw)' in 'mbcsToSbcs': dot substituted for <88>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <ce>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Burst size (β)' in 'mbcsToSbcs': dot substituted for <b2>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life indis (ϕn)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'Half-life targeted (ϕw)' in 'mbcsToSbcs': dot substituted for <95>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <cf>
Warning in grid.Call(C_textBounds, as.graphicsAnnot(x$label), x$x, x$y,  :
  conversion failure on 'RBC replenishment (ρ)' in 'mbcsToSbcs': dot substituted for <81>
Warning: Graphs cannot be horizontally aligned unless the axis parameter is set. Placing graphs unaligned.
ggsave(units = "px", dpi = 300, width = 2250, height = 1500, filename = here("code_repository/figures/mc_fitness_partition.tiff"), bg = "white", scale = 1.2)

#==========================================# # Supplementary figure on MC posterior parameter distribution #==========================================# ## Process data

## manually make dataframe containing the parameter values used in the deterministic model
par_det.df <- data.frame(
  parameter = c("rho", "phi_N1", "phi_N2", "iota_N1", "iota_N2", "burst"),
  deterministic = c(2.627156e-01, 3.520591e-02, 5.508420e+02, 1.669234e+01, 8.431785e-01, 5.721000e+00 )
)

## make into long format
posterior.long <- posterior.df %>% 
  filter(id %in% seq(0,1000,1)) %>% 
  tidyr::pivot_longer(-id, names_to = "parameter") %>% 
  left_join(par_det.df, by = "parameter") %>% ## add in the deterministic parameter values
  mutate(label = case_when(
    parameter == "rho" ~ "RBC replenishment (ρ)",
    parameter == "phi_N1" ~ "Half-life indis (ϕn)",
    parameter == "phi_N2" ~ "Half-life targeted (ϕw)",
    parameter == "iota_N1" ~ "Activation indis (ψn)",
    parameter == "iota_N2" ~ "Activation targeted (ψw)",
    parameter == "burst" ~ "Burst size (β)"
  )) ## rename paramter values

plot

## portion of parameter values that does not need log-transforming
posterior_1.pl <- ggplot(posterior.long %>% filter(!parameter %in% c("phi_N1", "phi_N2", "iota_N1"))) +
  geom_density(aes(x = value), fill = "grey") +
  geom_vline(aes(xintercept = deterministic), linetype = "dashed") +
  facet_wrap(~label, scales = "free") +
  labs(x = "", y = "Density") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())

## Plots where the x-axis should be log-transformed so it is easier to read
posterior_2.pl <- ggplot(posterior.long %>% filter(parameter %in% c("phi_N1", "phi_N2", "iota_N1"))) +
  geom_density(aes(x = value), fill = "grey") +
  geom_vline(aes(xintercept = deterministic), linetype = "dashed") +
  facet_wrap(~label, scales = "free") +
  labs(x = "Value", y = "Density") +
  scale_x_continuous(trans = "log10") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())

## plot together
ggarrange(posterior_1.pl, posterior_2.pl, ncol = 1, align = "hv")
  
## save
ggsave(units = "px", dpi = 300, width = 2000, height = 1300, filename = here("code_repository/figures/posterior.tiff"), bg = "white", scale = 1)

#==========================================# # Rank plot of parasite fitness with only one parameter varying #==========================================# ## Process data

## get median fitness values (normalized)
mc_single_fitness.sum2 <- mc_single_fitness.long2 %>% 
  group_by(short_label, id, parameter_label, parameter) %>% 
  summarize(median = median(fitness_norm),
            mean = mean(fitness_norm),
            geo_mean = exp(mean(log(fitness_norm))),
            fitness_det_norm = unique(fitness_det_norm)) %>% 
  left_join(select(mc_all_fitness_sum.df, short_label, geo_mean_all = geom_mean), by = "short_label") ## get geometric mean of when all parameter are varied
`summarise()` has grouped output by 'short_label', 'id', 'parameter_label'. You can override
using the `.groups` argument.

plot

note that it is quite a hassel to reorder cues when facetting. Hence, I will just plot out all 6 plots individually

# split database by parameter
mc_single_fitness_long2.ls <- split(mc_single_fitness.long2, mc_single_fitness.long2$parameter_label)
mc_single_fitness_sum2.ls <- split(mc_single_fitness.sum2, mc_single_fitness.sum2$parameter_label)

# plot out individual ggplot 
mc_single_plt.ls <- list()
for(i in seq(1,6,1)){
 mc_single_plt.ls[[i]] <- ggplot() +
  geom_violin(data = mc_single_fitness_long2.ls[[i]],
              aes(x = fct_reorder(short_label, geo_mean, .desc = T), y = fitness_norm), 
              fill = "grey", trim = T, color = "grey") +
  geom_point(data =  mc_single_fitness_sum2.ls[[i]],
             aes(x = short_label, y = mean, shape = "Mean", color = "Mean"), size = 2) +
  geom_point(data =  mc_single_fitness_sum2.ls[[i]],
             aes(x = short_label, y = geo_mean, shape = "Geometric mean\n(single parameter)", color = "Geometric mean\n(single parameter)"), size = 2) +
  geom_point(data =  mc_single_fitness_sum2.ls[[i]],
             aes(x = short_label, y = fitness_det_norm, shape = "Deterministic", color = "Deterministic"), size =2) +
   geom_point(data =  mc_single_fitness_sum2.ls[[i]],
             aes(x = short_label, y = geo_mean_all, shape = "Geometric mean\n(all parameters)", color = "Geometric mean\n(all parameters)"), size =2) +
  labs(x = "Cue(s)", y = "Normalized fitness", shape = "Legend", color = "Legend",
       subtitle = unique(mc_single_fitness_sum2.ls[[i]]$parameter_label)) +
  scale_color_manual(values = c("black", "#785EF0","#785EF0", "#FFB000")) +
   scale_shape_manual(values = c(16, 2, 17, 15))+
   ylim(0, 1.2) +
  theme_classic() +
  theme(axis.text.x=element_text(angle=45,hjust=1))
         
}

## arrange together
ggarrange(plotlist = mc_single_plt.ls, common.legend = T, ncol = 3, nrow = 2, align = "hv")

ggsave(units = "px", dpi = 300, width = 2500, height = 2000, filename = here("code_repository/figures/mc_single_fitness.tiff"), bg = "white", scale = 1)

#==================================# # Understanding the mechanism behind robustness #==================================# #——–Calculating deviations between cr and fitness———-# # Process single parameter dynamics data

# get list of files
mc_single_dyn.ls <- list.files(here("code_repository/data/mc_single_dyn"), pattern = "*.parquet", full.names = T)

# read in all of the files, filtering only for conversion rate
mc_single_dyn_cr.ls <- mclapply(mc_single_dyn.ls, function(x){
  df <- read_parquet(x)
  
  ## filter to retain only cr
  df_f <- df %>% filter(variable == "cr")
  return(df_f)
}, mc.cores = 6)

# combine
mc_single_dyn_cr.df <- do.call(rbind, mc_single_dyn_cr.ls)

# write
write_parquet(mc_single_dyn_cr.df, here("code_repository/data/mc_single_dyn_cr.parquet"))
mc_single_dyn_cr.df <- read_parquet(here("code_repository/data/mc_single_dyn_cr.parquet"))

# check # of records
mc_single_dyn_cr.df %>% group_by(id) %>% tally()

Process deterministic conversion rate time series dynamic

# single cue dynamics (conversion rate only)
si_cr.df <- si_dyn.df %>%
  filter(variable == "cr") %>%  ## get corresponding time steps (0.01) and cr only
  select(id, time, cr_det = value)

# dual cue dynamics (conversion rate only)
dual_cr.df <- dual_cue_dyn.df %>% 
  filter(variable == "cr") %>% 
  mutate(id = paste0(id, "-", id_b)) %>% 
  select(id, time, cr_det = value)

Calculate deviation in cr

mc_single_dyn_cr.sum
Error: object 'mc_single_dyn_cr.sum' not found

calculate loss of fitness due to variation

mc_single_dyn_cr.sum_fitness <- mc_single_fitness.sum %>% 
  mutate(geo_mean_diff = geo_mean-fitness_det_norm) %>% 
  left_join(mc_single_dyn_cr.sum, by = c("id", "parameter"))
Error in mutate(., geo_mean_diff = geo_mean - fitness_det_norm) : 
  object 'mc_single_fitness.sum' not found

isolate examples of robust and non-robust cues

## robust cue (I log)
mc_single_dyn_cr_join.df_p_robust <- mc_single_dyn_cr_join.df_p %>% 
  filter(id == "I_log" & parameter %in% c("beta", "psin") & row_number() %% 10 == 0) %>% 
  mutate(parameter_label = case_when(
    parameter == "psin" ~ "Activation indis (ψn)",
    parameter == "beta" ~ "Burst size (β)"
  ))
Error in filter(., id == "I_log" & parameter %in% c("beta", "psin") &  : 
  object 'mc_single_dyn_cr_join.df_p' not found

plot cr time series

mc_single_cr_dyn.plt1 <- ggplot(mc_single_dyn_cr_join.df_p_robust) +
  geom_line(aes(x = time, y = value, group = iter), alpha = 0.01) +
  geom_line(aes(x = time, y = cr_det, group = iter), alpha = 1, color = "#785EF0") +
  facet_wrap(~parameter_label) +
  ylim(0, 1) +
  labs(x = "", y = "Conversion rate") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.title.x = element_blank())

mc_single_cr_dyn.plt2 <- ggplot(mc_single_dyn_cr_join.df_p_notrobust) +
  geom_line(aes(x = time, y = value, group = iter), alpha = 0.01) +
  geom_line(aes(x = time, y = cr_det, group = iter), alpha = 1, color = "#785EF0") +
  facet_wrap(~parameter_label) +
  ylim(0, 1) +
  labs(x = "Time (days)", y = "Conversion rate") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())

plot relationship

# relationship between fitness vs robistness
ggplot(mc_single_dyn_cr.sum_fitness, aes(x = fitness_det_norm, y = geo_mean_diff, color = fitness_det_norm)) +
  geom_point(size = 2.5, color = "black", shape = 1) +
  geom_point(size = 2, alpha=0.8) +
  facet_wrap(~parameter_label, scales = "free") +
  scale_color_viridis_c() +
  labs(x = "Deterministic model fitness", y = "Normalized fitness difference\n(Single-Deterministic)", color = "Deterministic\nmodel fitness") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank())

# relationship between conversion rate difference and difference in fitness 
ggplot(mc_single_dyn_cr.sum_fitness, aes(x = median_cr_diff, geo_mean_diff, color = fitness_det_norm)) +
  geom_point(size = 2.5, color = "black", shape = 1) +
  geom_point(size = 2, alpha=0.8) +
  facet_wrap(~parameter_label) +
  scale_color_viridis_c() +
  ylim(NA, 0.02) +
  labs(x = "Median conversion rate difference", y = "Normalized fitness difference\n(Single-Deterministic)", color = "Deterministic\nmodel fitness") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "bottom")

ggsave(here("code_repository/figures/mc_single_cr_rel_int.tiff"), units = "px", dpi = 300, width = 2550, height = 1200, bg = "white", scale = 1)

# internal code to get positions of I log and R and I+Ig log
ggplot(mc_single_dyn_cr.sum_fitness, aes(x = median_cr_diff, geo_mean_diff, color = fitness_det_norm)) +
  geom_point(size = 2.5, color = "black", shape = 1) +
  geom_point(size = 2, alpha=0.8) +
  geom_text(data = mc_single_dyn_cr.sum_fitness %>% filter(id %in% c("I_log", "R_none-I+Ig_log")), aes(x = median_cr_diff, y =geo_mean_diff, label = id)) +
  facet_wrap(~parameter_label) +
  scale_color_viridis_c() +
  ylim(NA, 0.02) +
  labs(x = "Median conversion rate difference", y = "Normalized fitness difference\n(Single-Deterministic)", color = "Deterministic\nmodel fitness") +
  theme_bw() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "bottom")

plot together

# conversion rate dynamic
ggarrange(mc_single_cr_dyn.plt1, mc_single_cr_dyn.plt2, nrow = 2, align = "v", labels = c("B", "C"))
ggsave(here("code_repository/figures/mc_single_cr_dyn_int.tiff"), units = "px", dpi = 300, width = 2550*(2/3), height = 1000, bg = "white", scale = 1)

#=====================================# # Impact of cue perception on virulence #====================================# #——— Divid dynamics data into high and low performing —————# # Single cue

Dual cue

# classify top 4 performing dual cues as "high performing"
top_4_dual_cue <- dual_cue_f_final.df %>% 
  mutate(label_alt = paste(label, "&" , label_b)) %>% 
  slice_max(n = 4, order_by = fitness)

# get wide format dataframe. Here, we are interested in the RBC and total iRBC count
dual_dc.df <- dual_cue_dyn_30.df %>% 
  mutate(label_alt = paste(label, "&" , label_b)) %>% ## get new label
  filter(variable == "I" | variable == "Ig" | variable == "R") %>% ## get desired variables
  tidyr::pivot_wider(names_from = variable, values_from = value, id_cols = c(time, label_alt)) %>%
  mutate(total = I+Ig)

# split into top and poor-performing cues
dual_dc.high <- dual_dc.df %>% filter(label_alt %in% top_4_dual_cue$label_alt)

# reorder cues by descending fitness
dual_dc.high$label_alt <- factor(dual_dc.high$label_alt, top_4_dual_cue$label_alt)

#————Get reference dynamics—————# # the ideal dynamic (time as cue, df = 9)

time_high.dyn <- chabaudi_si_clean_high(
  parameters_cr = c(9.154314,  -7.570829, -22.506638 ,  3.382405 ,-13.453519 ,-17.011485  , 3.678181, -12.851895 ,-26.115158),
  immunity = "tsukushi",
  parameters = parameters_tsukushi,
  time_range = seq(0, 30, by = 1e-3),
  cue_range =  seq(0, 20, by = 1e-3),
  cue = "t",
  solver = "vode",
  dyn = T)
[1] "Time is chosen as cue. Cue_range must equal to time_range"

#————-Plot disease curves—————–# # Single cue

dual cue

#———area of curve vs fitness—————# # function to calculate area within curve

get_dc_area <- function(df){
  x <- df$total
  y <- df$R
  chull(x,y)->i
  return(areapl(cbind(x[i],y[i])))
}

single cue area

# split df
si_dc.ls <- split(si_dc.df, si_dc.df$id)

# get area
si_dc.area <- cbind.data.frame(area = as.numeric(lapply(si_dc.ls, get_dc_area)), id = names(lapply(si_dc.ls, get_dc_area)))

# join with fitness
si_opt_area.df <- si_opt.df %>% 
  filter(cue != "t") %>% 
  left_join(si_dc.area, by = "id") %>% 
  mutate(fitness_norm = fitness_20/9.883602) ## normalize fitness (use fitness obtained using time df=9 given that we are plotting dual cue and single on the same graph!)

# sanity check area. The area should be on the order if we assume a rectangle!
si_dc.df %>% 
  group_by(id) %>% 
  summarise(R_diff = max(R)-min(R),
            total_diff = max(total)-min(total),
            product = R_diff*total_diff)

dual cue

# split
dual_dc.ls <- split(dual_dc.df, dual_dc.df$label_alt)

# get area
dual_dc.area <- cbind.data.frame(area = as.numeric(lapply(dual_dc.ls, get_dc_area)), label_alt = names(lapply(dual_dc.ls, get_dc_area)))

# join with fitness
dual_cue_f_final_area.df <- dual_cue_f_final.df %>% 
  mutate(label_alt = gsub("I\\+Ig", "Total I", paste(label, "&" , label_b))) %>% 
  left_join(dual_dc.area, by = c("label_alt")) %>% 
  mutate(fitness_norm = fitness/9.883602) ## normalize fitness (use fitness obtained using time df=9 given that we are plotting dual cue and single on the same graph!)

get area for ideal and static cr

# time
time_high.area <- get_dc_area(time_high.vir)

fit GLM

Note that static and time based cue is not included in the model!

# fit lm
si_lm <- lm(fitness_norm ~ area, data = si_opt_area.df)
dual_lm <- lm(fitness_norm ~ area, data = dual_cue_f_final_area.df)

# get prediction with 95% CI
si_lm.ci = predict(si_lm, interval = "confidence") %>% cbind.data.frame(area = si_opt_area.df$area)
dual_lm.ci = predict(dual_lm, interval = "confidence") %>% cbind.data.frame(area = dual_cue_f_final_area.df$area)

# look at R^2
summary(si_lm)$r.squared ## 0.1765248
[1] 0.1765248
summary(dual_lm)$r.squared ## 0.1053591
[1] 0.1053591

plot correlation

ggplot() +
  geom_point(data = dual_cue_f_final_area.df, aes(x = area, y = fitness_norm, color = "Dual cue", shape = "Dual cue"), size = 3, alpha = 0.7) +
  geom_point(aes(x = time_high.area, y = max(time_high.dyn %>% filter(variable == "tau_cum" & time == 20) %>% pull(value))/9.883602, color = "Time", shape = "Time"), size = 3) +
  geom_line(data = dual_lm.ci, aes(x = area, fit)) +
  geom_ribbon(data = dual_lm.ci, aes(x = area, ymin = lwr, ymax = upr), alpha = .15) +
  annotate(geom = "text", label = "R^2=0.11", x = 1.6*(10^11), y = 0.76) +
  annotate(geom = "text", label = "Time", x = 2.7*(10^11), y = 0.98) +
  labs(x = "Area", y = "Normalized fitness", shape = "Cue category", color = "Cue category") +
  scale_color_manual(values = c(blue, "black")) +
  xlim(min(si_opt_area.df$area), max(dual_cue_f_final_area.df$area)) +
  theme_classic() +
  theme(legend.position = "none")
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_point()`).
Warning: Removed 2 rows containing missing values or values outside the scale range (`geom_line()`).

#———assemble figure————#

#=============================# # Validation of optimization #=============================# #—— process input data ——# # Process data

Plot

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpUaGlzIGZpbGUgd2lsbCB0YWtlIHlvdSB0aHJvdWdoIHJlY3JlYXRpbmcgdGhlIGZpZ3VyZXMgcHJlc2VudGVkIGluIHRoZSBtYW51c2NyaXB0LiBOb3RlIHRoYXQgc29tZSBvZiB0aGUgZmlndXJlcyB3ZW50IHRocm91Z2ggYWRkaXRpb25hbCBlZGl0cyBpbiBJbGx1c3RyYXRvciBmb3IgYWRkaXRpb25hbCBsYWJlbGxpbmchCgojIFByZXBhcmF0aW9uCiMjIExvYWQgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShmb3JjYXRzKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoZGVTb2x2ZSkKbGlicmFyeShjcm9uZSkKbGlicmFyeShvcHRpbVBhcmFsbGVsKQpsaWJyYXJ5KGRvUGFyYWxsZWwpCmxpYnJhcnkoZG9STkcpCmxpYnJhcnkoYXJyb3cpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShwYXJhbGxlbCkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KGJheWVzdGVzdFIpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3BsaW5lczIpCmxpYnJhcnkoc3BsYW5jcykKYGBgCgojIyBsb2FkIHZhcmlhYmxlcwpgYGB7cn0KcGFyYW1ldGVyc190c3VrdXNoaSA8LSBjKFIxID0gOC44OSoxMF42LCAKICAgICAgICAgICAgICAgIGxhbWJkYSA9IDMuNyoxMF41LAogICAgICAgICAgICAgICAgbXUgPSAwLjAyNSwgCiAgICAgICAgICAgICAgICBwID0gOCoxMF4tNiwgCiAgICAgICAgICAgICAgICBhbHBoYSA9IDEsIAogICAgICAgICAgICAgICAgYWxwaGFnID0gMiwgCiAgICAgICAgICAgICAgICBiZXRhID0gNS43MjEsIAogICAgICAgICAgICAgICAgbXVtID0gNDgsIAogICAgICAgICAgICAgICAgbXVnID0gNCwgCiAgICAgICAgICAgICAgICBJMCA9IDQzLjg1OTY1LCAKICAgICAgICAgICAgICAgIElnMCA9IDAsIAogICAgICAgICAgICAgICAgYSA9IDE1MCwgCiAgICAgICAgICAgICAgICBiID0gMTAwLCAKICAgICAgICAgICAgICAgIHNwID0gMSwKICAgICAgICAgICAgICAgIHBzaW4gPSAxNi42OTIzNCwKICAgICAgICAgICAgICAgIHBzaXcgPSAwLjg0MzE3ODUsCiAgICAgICAgICAgICAgICBwaGluID0gMC4wMzUyMDU5MSwgCiAgICAgICAgICAgICAgICBwaGl3ID0gNTUwLjg0MiwKICAgICAgICAgICAgICAgIGlvdGEgPSAyLjE4KigxMF42KSwKICAgICAgICAgICAgICAgIHJobyA9IDAuMjYyNzE1NikKCiMgaW1wb3J0IGluIGRhdGEgZmlsZXMKZXpfbGFiZWwgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZXpfbGFiZWwuY3N2IikpICMjIGxhYmVsbGluZyBzY2hlbmUKc2lfb3B0LmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX29wdC5jc3YiKSkgIyMgb3B0aW1pemVkIHBhcmFtZXRlciArIGZpdG5lc3MgbGlzdApzaV9keW4uZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX2R5bi5wYXJxdWV0IikpICMjIGR5bmFtaWNzIG9mIHNpbmdsZSBjdWUgbW9kZWxzCnNpX3JuLmRmIDwtICByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfcm4ucGFycXVldCIpKSAjIyByZWFjdGlvbiBub3JtcyBvZiBzaW5nbGUgY3VlIG1vZGVscwpzaV9ydWcuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX3J1Zy5wYXJxdWV0IikpICMjIGRhdGEgZm9yIHJ1ZyBwbG90cwptY19hbGxfZml0bmVzcy5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfYWxsX2ZpdG5lc3MucGFycXVldCIpKSAjIyBmaXRuZXNzIHZhbHVlcyBmb3IgbWMgd2hlbiBhbGwgcGFyYW1ldGVycyBhcmUgdmFyeWluZwptY19zaW5nbGVfZml0bmVzcy5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvbWNfc2luZ2xlX2ZpdG5lc3MucGFycXVldCIpKSAjIyBmaXRuZXNzIHZhbHVlcyBmb3IgbWMgd2hlbiBvbmx5IG9uZSBwYXJhbWV0ZXIgaXMgdmFyeWluZwpkdWFsX2N1ZV9mX2xjLmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfY3VlX2ZpdG5lc3NfbG9jYWwuY3N2IikpICMjIG9wdGltaXplZCBkdWFsIGN1ZSBzdHJhdGVneSB1c2luZyBMLUJGR1MtQgpkdWFsX2N1ZV9mX2dsYi5kZiA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9maXRuZXNzX2dsb2JhbC5jc3YiKSkgIyMgb3B0aW1pemVkIGR1YWwgY3VlIHN0cmF0ZWd5IHVzaW5nIERFb3B0aW0gKyBMLUJGRwpkdWFsX2N1ZV9mX2ZpbmFsLmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfY3VlX2ZpdG5lc3NfZmluYWwuY3N2IikpICMjIGZpbmFsIGRhdGFmcmFtZSBjb250YWluaW5nIHN0cmF0ZWd5IHRoYXQgcHJvZHVjZWQgaGlnaGVzdCBmaXRuZXNzIChkdWFsIGN1ZSBtb2RlbHMpCmR1YWxfc2VsZWN0ZWRfY3IuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfc2VsZWN0ZWRfY3IucGFycXVldCIpKSAjIyBjciBkeW5hbWljcyBvZiBzZWxlY3RlZCBtb2RlbHMKY3VlX3JhbmdlX3NpX2FsdC5kZiA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9jdWVfcmFuZ2Vfc2lfYWx0LmNzdiIpKSAjIyBjdWUgcmFuZ2VzIGZvciBkdWFsIGN1ZSBtb2RlbHMKZHVhbF9jdWVfZHluLmRmIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW4ucGFycXVldCIpKSAjIyBkeW5hbWljcyBvZiBkdWFsIGN1ZSBtb2RlbHMKZXhwX3NzLmRmIDwtIHJlYWQuY3N2KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2V4cGVyaW1lbnRhbF9kYXRhLmNzdiIpKSAjIyBjbGVhbmVkIGV4cGVyaW1lbnRhbCByZWNvcmRzIG9mIFAuIGNoYWJhdWRpIGluZmVjdGlvbgpwb3N0ZXJpb3IuZGYgPC0gcmVhZC5jc3YoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvcG9zdGVyaW9yLmNzdiIpKSAjIyBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIG9mIGRpZmZlcmVudCBwYXJhbWV0ZXIgdmFsdWVzCnNpX2R5bl8zMC5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfZHluXzMwLnBhcnF1ZXQiKSkgIyMgc2luZ2xlIGN1ZSBkeW5hbWljcyBkYXRhICgzMCBkYXlzKQpkdWFsX2N1ZV9keW5fMzAuZGYgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL2R1YWxfY3VlX2R5bl8zMC5wYXJxdWV0IikpICMjIGR1YWwgY3VlIGR5bmFtaWNzICgzMCBkYXlzKQptY19zaW5nbGVfZHluX2NyX2pvaW4uZGZfcHIgPC0gcmVhZF9wYXJxdWV0KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX3NpbmxlX2R5bl9jcl9qb2luLnBhcnF1ZXQiKSkgIyMgbWMgZHluYW1pY3MgKHNpbmdsZSBwYXJhbWV0ZXIgdmFyaWF0aW9uKSB3aXRoIG9ubHkgY3IgYWxvbmcgd2l0aCBkZXRlcm1pbnN0aWMgbW9kZWwgY3IKbWNfc2luZ2xlX2R5bl9jci5zdW1yZSA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZHluX2NyX3N1bS5jc3YiKSkgIyMgbWMgZHluYW1pY3MgKHNpbmdsZSBwYXJhbWV0ZXIpIGNyIHN1bW1hcnkKCiMjIHZhbGlkYXRpb24gZGF0YSAoY29tcGFyaW5nIGZpdG5lc3MgdG8gcmFuZG9tIHNwbGluZSkKdmFsaWRhdGlvbi5scyA8LSBsaXN0LmZpbGVzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL3NpX3ZhbGlkYXRpb24vIiksIGZ1bGwubmFtZXMgPSBUKQp2YWxpZGF0aW9uLmRmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseSh2YWxpZGF0aW9uLmxzLCByZWFkLmNzdikpCgojIGltcG9ydCBpbiBjb2RlCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL2NoYWJhdWRpX3NpX2NsZWFuLlIiKSkKc291cmNlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9mdW5jdGlvbnMvcGFyX3RvX2RmLlIiKSkKc291cmNlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9mdW5jdGlvbnMvY2hhYmF1ZGlfc2lfY2xlYW5faGlnaC5SIikpCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL3Bhcl90b19obS5SIikpCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL3Bhcl90b19obV90ZS5SIikpCnNvdXJjZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZnVuY3Rpb25zL2NoYWJhdWRpX3NpX3N0YXRpYy5SIikpCgojIGNvbG9yIGNvZGVzCm9yYW5nZSA8LSAiI2ZjOGQ1OSIKYmx1ZSA8LSAiIzQ1NzViNCIKYGBgCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIEN1ZSBwZXJjZXB0aW9uIG1lZGlhdGVzIGZpdG5lc3MKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIy0tLS0tLS0tLS0tLSBBLiBjb252ZXJzaW9uIHJhdGUgZHluYW1pY3MgYW5kIGZpdG5lc3MgdmFsdWVzIC0tLS0tLS0tLS0tLSMKIyMgZnVuY3Rpb24gZm9yIGR5bmFtaWNzIGdlbmVyYXRpb24KYGBge3J9CiMgZnVuY3Rpb24gZm9yIGdldHRpbmcgc2luZ2xlIGN1ZSBpbmZlY3Rpb24gZHluYW1pY3MKZ2V0X3NpX2R5biA8LSBmdW5jdGlvbihkZil7CiAgIyMgcHJvY2Vzc2luZyBtb2RlbCBpbnB1dAogIHBhciA8LSBjKGRmJHZhcjEsIGRmJHZhcjIsIGRmJHZhcjMsIGRmJHZhcjQpICMjIHBhcmFtZXRlcnMKICBjdWUgPC0gZGYkY3VlICMjIGN1ZSBjaG9pY2UKICBsb2cgPC0gaWZlbHNlKGRmJGxvZz09ImxvZyIsICJsb2cxMCIsICJub25lIikgIyMgbG9nIG9yIG5vdAogIGN1ZV9yYW5nZSA8LSBzZXEoZGYkbG93LCBkZiRoaWdoLCBieSA9IGRmJGJ5KSAjIyBjdWUgcmFuZ2UKICBpZCA8LSBkZiRpZCAjIyBpZAogIAogICMjIGdldCBkeW5hbWljcyBkYXRhCiAgZHluIDwtIGNoYWJhdWRpX3NpX2NsZWFuKAogICAgICAgICAgICBwYXJhbWV0ZXJzX2NyID0gcGFyLCAKICAgICAgICAgICAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGksIAogICAgICAgICAgICB0aW1lX3JhbmdlID0gc2VxKDAsIDIwLCAwLjAwMSksIAogICAgICAgICAgICBjdWUgPSBjdWUsIAogICAgICAgICAgICBjdWVfcmFuZ2UgPSBjdWVfcmFuZ2UsIAogICAgICAgICAgICBsb2dfY3VlID0gbG9nLAogICAgICAgICAgICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgICAgICAgICAgIHNvbHZlciA9ICJ2b2RlIiwKICAgICAgICAgICAgZHluID0gVFJVRSkKICAKICAjIGFwcGVuZCBpZAogIGR5bjIgPC0gY2JpbmQoZHluLCBpZCA9IHJlcChpZCwgbnJvdyhkeW4pKSkKICAKICAjIHJldHVybiByZXN1bHRzCiByZXR1cm4oZHluMikKfQpgYGAKCiMjIHJ1biBmdW5jdGlvbiB0byBnZW5lcmF0ZSBkYXRhCmBgYHtyfQojIyBzcGxpdCBzaV9vcHQgaW50byBsaXN0CnNpX29wdC5scyA8LSBzcGxpdChzaV9vcHQuZGYsIHNlcShucm93KHNpX29wdC5kZikpKQoKIyMgZ2V0IGR5bmFtaWNzCnNpX2R5biA8LSBtY2xhcHBseShzaV9vcHQubHMsIGdldF9zaV9keW4sIG1jLmNvcmVzID0gOCkKCiMjIGNvbWJpbmUgdGhlIGR5bmFtaWNzIGZpbGUKc2lfZHluLmRmIDwtIGRvLmNhbGwocmJpbmQsIHNpX2R5bikKCiMjIHNhdmUKIyB3cml0ZV9wYXJxdWV0KHNpX2R5bi5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvc2lfZHluLnBhcnF1ZXQiKSkKYGBgCgojIyBnZXQgMzAgZGF5IHNpbXVsYXRpb24gZGF0YSAobm90IHVzZWQgZm9yIGZpdG5lc3MgYnV0IGZvciBwbG90dGluZyBwdXJwb3NlcykKYGBge3J9CiMjIGdldCBkeW5hbWljcwpzaV9vcHQubHMyIDwtIHNwbGl0KHNpX29wdC5kZiAlPiUgZmlsdGVyKGN1ZSAhPSAidCIpLCBzZXEobnJvdyhzaV9vcHQuZGYlPiUgZmlsdGVyKGN1ZSAhPSAidCIpKSkpCnNpX2R5bl8zMCA8LSBtY2xhcHBseShzaV9vcHQubHMyLCBnZXRfc2lfZHluLCBtYy5jb3JlcyA9IDgpCgojIyBjb21iaW5lIHRoZSBkeW5hbWljcyBmaWxlCnNpX2R5bl8zMC5kZiA8LSBkby5jYWxsKHJiaW5kLCBzaV9keW5fMzApCnNpX2R5bl8zMC5kZiAlPiUgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UobWF4X3RpbWUgPSBtYXgodGltZSkpCgojIyBzYXZlCndyaXRlX3BhcnF1ZXQoc2lfZHluXzMwLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9zaV9keW5fMzAucGFycXVldCIpKQpgYGAKCiMjIHByb2Nlc3MgZGF0YSB0byBpc29sYXRlIGNvbnZlcnNpb24gcmF0ZSBhbmQgcGFpciB0aGVtIHdpdGggZml0bmVzcwpgYGB7cn0KIyMga2VlcCBvbmx5IGNvbnZlcnNpb24gcmF0ZSBmcm9tIGR5bmFtaWNzCnNpX2R5bl9jci5kZiA8LSBzaV9keW4uZGYgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiY3IiKQoKIyMgbGVmdCBqb2luIHdpdGggZml0bmVzcyB2YWx1ZXMKc2lfZHluX2NyX2ZpdG5lc3MuZGYgPC0gc2lfZHluX2NyLmRmICU+JSAKICBsZWZ0X2pvaW4oc2lfb3B0LmRmLCBieSA9ICJpZCIpIAoKIyMgbm9ybWFsaXplIGZpdG5lc3MgYnkgZGl2aWRpbmcgYWxsIHZhbHVlcyBieSB0aGUgZml0bmVzcyB2YWx1ZXMgd2UgZ2V0IHdoZW4gY3VlID0gdGltZQpzaV9vcHQuZGYgPC0gc2lfb3B0LmRmICU+JSAKICBtdXRhdGUoZml0bmVzc19ub3JtID0gZml0bmVzc18yMCAvIHNpX29wdC5kZltzaV9vcHQuZGYkY3VlID09ICJ0IixdJGZpdG5lc3NfMjApCmBgYAoKIyMgcGxvdApgYGB7cn0KIyMgZml0bmVzcyB2YWx1ZXMgcmFua2VkIGZyb20gaGlnaCB0byBsb3cKZml0bmVzc19yYW5rLnBsdCA8LSBnZ3Bsb3Qoc2lfb3B0LmRmLCBhZXMoeCA9IGZpdG5lc3Nfbm9ybSwgeSA9IGZjdF9yZW9yZGVyKGxvbmdfbGFiZWwsIGZpdG5lc3NfMjApKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImJsYWNrIikgKwogIGxhYnMoeCA9ICJOb3JtYWxpemVkIGZpdG5lc3MiLCB5ID0gIkN1ZSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gbWFyZ2luKDUuNSwgMCwgNS41LCA1LjUsICJwdCIpKQoKIyMgY29udmVyc2lvbiByYXRlIGR5bmFtaWMgcmFua2VkIGluIHRoZSBzYW1lIG9yZGVyCmZpdG5lc3NfY3IucGx0IDwtIGdncGxvdChzaV9keW5fY3JfZml0bmVzcy5kZiwgYWVzKHggPSB0aW1lLCB5ID0gZmN0X3Jlb3JkZXIobG9uZ19sYWJlbCwgZml0bmVzc18yMCksIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3Jhc3RlcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsKICBsYWJzKGZpbGwgPSAiVHJhbnNtaXNzaW9uXG5pbnZlc3RtZW50IiwgeCA9ICJUaW1lIChkYXlzKSIpICsKICB4bGltKDEsIDIwKSArICMgbm90ZSB0aGF0IGNyIGF0IGRheSAwLT4xIGlzIGFsd2F5cyAwIGR1ZSB0byBob3cgdGhlIG1vZGVsIGlzIHNldCB1cCEKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLCAgI3JlbW92ZSB5IGF4aXMgbGFiZWxzCiAgICAgICAgYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKSAgI3JlbW92ZSB5IGF4aXMgdGlja3MKICAgICAgICApCgojIyBwbG90IHRvZ2V0aGVyCmZpdG5lc3NfcmFua19jci5wbHQgPC0gZ2dhcnJhbmdlKGZpdG5lc3NfcmFuay5wbHQsIGZpdG5lc3NfY3IucGx0LCB3aWR0aHMgPSBjKDEuMiwgMSksIGFsaWduID0gImgiLCBjb21tb24ubGVnZW5kID0gVCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kID0gImJvdHRvbSIpCmBgYAoKIy0tLS0tLS0tLS0tLSBCLiBSZWFjdGlvbiBub3JtcyAtLS0tLS0tLS0tLS0jCiMjIGZ1bmN0aW9uIHRvIG9idGFpbiB0aGUgY3VlIHZhbHVlcyBzZW5zZWQgYnkgcGFyYXNpdGVzIGZyb20gZHluYW1pY3MgZGF0YXMKYGBge3J9CmdldF9ydWcgPC0gZnVuY3Rpb24oZGYpewogICMjIHByb2Nlc3MgY3VlCiAgY3VlIDwtIHVuaXF1ZShkZiRjdWUpCiAgCiAgIyMgaWYgY3VlIGNvbnRhaW5zICIrIiwgd2UgbmVlZCB0byBmaXJzdCBzcGxpdCB0aGVtIHVwIGFuZCBhZGQgdGhlbSBpbnRvIHRoZSBmaW5hbCBkYXRhZnJhbWUKICAgaWYoc3RyaW5ncjo6c3RyX2RldGVjdChjdWUsICJcXCsiKSl7CiAgICBjdWVfc3BsaXQgPC0gc3RyaW5ncjo6c3RyX3NwbGl0KHN0cmluZyA9IGN1ZSwgcGF0dGVybiA9ICJcXCsiLCBzaW1wbGlmeSA9IFQpCiAgICAjIyBnZXQgdGhlIHR3byBjdWVzIAogICAgY3VlX3RlbXBfMSA8LSBjdWVfc3BsaXRbWzFdXQogICAgY3VlX3RlbXBfMiA8LSBjdWVfc3BsaXRbWzJdXQogICAgIyMgZmlsdGVyIGR5bgogICAgcnVnIDwtIGRmICU+JSAKICAgICAgZmlsdGVyKHZhcmlhYmxlID09IGN1ZV90ZW1wXzEgfCB2YXJpYWJsZSA9PSBjdWVfdGVtcF8yKSAlPiUgCiAgICAgIGRwbHlyOjpncm91cF9ieSh0aW1lKSAlPiUgCiAgICAgIGRwbHlyOjptdXRhdGUoc3VtID0gc3VtKHZhbHVlLCBuYS5ybSA9IFQpKSAlPiUgCiAgICAgIHNlbGVjdCh0aW1lLCB2YWx1ZSA9IHN1bSwgaWQpCiAgfQogIAogICMgZm9yIGN1ZSB3aXRoIG5vIGFkZGl0aW9uLCBpdCBpcyBzaW1wbHkgZmlsdGVyaW5nIGZvciB0aGUgdmFsdWVzIGFuZCByZXR1cm5pbmcgaXQKICBpZihzdHJpbmdyOjpzdHJfZGV0ZWN0KGN1ZSwgIlxcKyIsIG5lZ2F0ZSA9IFQpKXsKICAgIHJ1ZyA8LSBkZiAlPiUgCiAgICAgIGRwbHlyOjpmaWx0ZXIodmFyaWFibGUgPT0gY3VlKSAlPiUgCiAgICAgIGRwbHlyOjpzZWxlY3QodGltZSwgdmFsdWUsIGlkKX0KICAKICByZXR1cm4ocnVnKQp9CmBgYAoKIyMgcnVuIGZ1bmN0aW9uIHRvIG9idGFpbiBjdWUgdmFsdWVzCmBgYHtyfQojIGpvaW4gZHluYW1pY3MgZGF0YSB3aXRoIGN1ZSBpbmZvcm1hdGlvbgpzaV9keW5fY3VlLmRmIDwtIGxlZnRfam9pbihzaV9keW4uZGYsIHNpX29wdC5kZiwgYnkgPSAiaWQiKQoKIyBzcGxpdCBiYXNlZCBvbiBpbmRpdmlkdWFsIGxhYmVscwpzaV9keW5fY3VlLmxzIDwtIHNpX2R5bl9jdWUuZGYgJT4lIGdyb3VwX3NwbGl0KGlkKQoKIyBydW4gZnVuY3Rpb24gdG8gZ2V0IHJ1ZwpzaV9ydWcgPC0gbWNsYXBwbHkoc2lfZHluX2N1ZS5scywgZ2V0X3J1ZywgbWMuY29yZXMgPSA2KQoKIyBjb21iaW5lIGFuZCBzYXZlCnNpX3J1Zy5kZiA8LSBkby5jYWxsKHJiaW5kLCBzaV9ydWcpCndyaXRlX3BhcnF1ZXQoc2lfcnVnLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9zaV9ydWcucGFycXVldCIpKQpgYGAKCiMjIGZ1bmN0aW9uIHRvIG9idGFpbiByZWFjdGlvbiBub3JtCmBgYHtyfQpnZXRfc2lfcm4gPC0gZnVuY3Rpb24oZGYpewogICMjIHJlYWQgaW4gdGhlIHBhcmFtZXRlciBzZXRzCiAgcGFyIDwtIGMoZGYkdmFyMSwgZGYkdmFyMiwgZGYkdmFyMywgZGYkdmFyNCkKICAjIyBnZXQgY3VlIHJhbmdlCiAgY3VlX3JhbmdlIDwtIHNlcShkZiRsb3csIGRmJGhpZ2gsIGJ5ID0gZGYkYnkpCiAgIyMgbmVzdGVkIGZ1bmN0aW9uIHRvIGNvbnZlcnQgcGFyYW1ldGVyIHNldCBpbnRvIGJhc2lzIHNwbGluZSBmdW5jdGlvbgogIHJuIDwtIHBhcl90b19kZihwYXIgPSBwYXIsIGN1ZV9yYW5nZSA9IGN1ZV9yYW5nZSkKICAKICAjIGlmIHBhcmFzaXRlIGlzIHNlbnNpbmcgbG9nZ2VkIGN1ZSwgd2UgaGF2ZSB0byBleHBvbmVudGlhdGUgaXQgYmFjayBzbyB0aGV5IGFyZSBvbiB0aGUgc2FtZSBzY2FsZSEgCiAgcm4yIDwtIHJuCiAgaWYoc3RyaW5ncjo6c3RyX2RldGVjdChkZiRsb2csICJsb2ciKSl7cm4yJGN1ZV9yYW5nZSA8LSAxMF4ocm4yJGN1ZV9yYW5nZSl9CgogICMgYXBwZW5kIGxhYmVsIHRvIHJlYWN0aW9uIG5vcm0sIGR5biwgYW5kIHJ1ZwogIHJuMiA8LSBkYXRhLmZyYW1lKHJuMiwgaWQgPSBkZiRpZCkKICAKICByZXR1cm4ocm4yKQp9CmBgYAoKIyMgcnVuIGZ1bmN0aW9uIHRvIGdldCByZWFjdGlvbiBub3JtcwpgYGB7cn0KIyBzcGxpdCBkYXRhZnJhbWUgb2Ygb3B0aW1pemVkIHJjdWVzCnNpX29wdC5scyA8LSBzcGxpdChzaV9vcHQuZGYsIHNlcShucm93KHNpX29wdC5kZikpKQoKIyBydW4gZnVuY3Rpb24Kc2lfcm4gPC0gbGFwcGx5KHNpX29wdC5scywgZ2V0X3NpX3JuKQoKIyBiaW5kIHRvZ2V0aGVyCnNpX3JuLmRmIDwtIGRvLmNhbGwocmJpbmQsIHNpX3JuKQp3cml0ZV9wYXJxdWV0KHNpX3JuLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9zaV9ybi5wYXJxdWV0IikpCmBgYAoKIyMgcHJlcGFyaW5nIHRoZSBkYXRhc2V0IGZvciBwbG90dGluZyByZWFjdGlvbiBub3JtcyBhbmQgcnVncwpgYGB7cn0KIyMgd2UgYXJlIG9ubHkgcGxvdHRpbmcgcmVsZXZlbnQgcmFuZ2VzIG9mIHRoZSByZWFjdGlvbiBub3JtLCBtZWFuaW5nIHRoYXQgdGhlc2UgYXJlIHRoZSBjdWUgdmFsdWVzIGFjdHVhbGx5ICJzZW5zZWQiIGJ5IHRoZSBwYXJhc2l0ZXMgaW4gYW4gaW5mZWN0aW9uLiBXZSBjYW4gdXNlIHRoZSBydWcgZGF0YWZyYW1lIHRvIGdldCBhIHJvdWdoIGVzdGltYXRlIG9mIHRoYXQKc2lfcnVnX2xpbS5kZiA8LSBzaV9ydWcuZGYgJT4lIAogIGdyb3VwX2J5KGlkKSU+JSAKICBzdW1tYXJpc2UobWluX3RlbXAgPSBtaW4odmFsdWUsIG5hLnJtID0gVCkqMC45LAogICAgICAgICBtYXhfdGVtcCA9IG1heCh2YWx1ZSwgbmEucm0gPSBUKSoxLjEpICU+JSAjIyBjYWxjdWxhdGUgbWluIGFuZCBtYXggZm9yIGVhY2ggY3VlCiAgbGVmdF9qb2luKHNpX29wdC5kZiwgYnkgPSAiaWQiKSAlPiUgIyMgam9pbiB3aXRoIHNpX29wdCBzbyB3ZSBjYW4gZ2V0IHRoZSBjbW1vbiBjdWUKICB1bmdyb3VwKCkgJT4lIAogIGdyb3VwX2J5KGN1ZSkgJT4lIAogIHN1bW1hcmlzZShtaW4gPSBtaW4obWluX3RlbXApLCBtYXggPSBtYXgobWF4X3RlbXApKSAKCiMjIHByb2Nlc3MgcmVhY3Rpb24gbm9ybSBkYXRhIHRvIHJlc3RyaWN0IHJhbmdlIGFuZCBnZXQgbGFiZWwKc2lfcm4uZGZfcCA8LSBzaV9ybi5kZiAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChzaV9vcHQuZGYsIGlkLCBjdWUsIGxvZyksIGJ5ID0gImlkIikgJT4lICMjIGdldCBjdWUgYW5kIGxvZyBzdGF0dXMgZnJvbSBzaV9vcHQuZGYKICBsZWZ0X2pvaW4oc2lfcnVnX2xpbS5kZiwgYnkgPSAiY3VlIikgJT4lICMjIHZpYSBjdWUsIGdldCByYW5nZXMgd2Ugd2FudCB0byBsaW1pdCB0aGUgcm4gdG8KICBmaWx0ZXIoY3VlX3JhbmdlIDw9IG1heCAmIGN1ZV9yYW5nZSA+PSBtaW4pICU+JSAjIyBrZWVwIG9ubHkgY3VlIHZhbHVlcyB3aXRoaW4gcmFuZ2UKICBsZWZ0X2pvaW4oc2VsZWN0KGV6X2xhYmVsLCBpZCwgY3VlX2xhYmVsKSwgYnkgPSAiaWQiKSAjIGdldCBsYWJlbHMKCgojIyByZWZhY3RvciB0byByZW9yZGVyIHRoZSBvcmRlciBhdCB3aGljaCB0aGUgY3VlcyBhcmUgcHJlc2VudGVkCnNpX3JuLmRmX3AkY3VlX2xhYmVsIDwtIGZhY3RvcihzaV9ybi5kZl9wJGN1ZV9sYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkFzZXh1YWwgaVJCQyIsICJTZXh1YWwgaVJCQyIsICJUb3RhbCBpUkJDIiwgIkdhbWV0b2N5dGUiLCAiUkJDIikpCgojIyBzcGxpdCBydWcgbGFiZWxzIGJ5IG5vbmUgbG9nZ2VkIGFuZCBsb2dnZWQKc2lfcnVnX2N1ZS5kZiA8LSBzaV9ydWcuZGYgJT4lIAogIGRpc3RpbmN0KGlkLCB2YWx1ZSkgJT4lICMjIGN1dCBkb3duIG9uIG51bWJlciBvZiBkYXRhcG9pbnRzLiBGb3IgZWFjaCBpZCwga2VlcCBvbmx5IGRpc3RpbmN0IHBvaW50cwogIGxlZnRfam9pbihzZWxlY3Qoc2lfb3B0LmRmLCBpZCwgY3VlLCBsb2cpLCBieSA9ICJpZCIpICU+JSAjIyBnZXQgY3VlIGFuZCBsb2cgc3RhdHVzIGZyb20gc2lfb3B0LmRmCiAgbGVmdF9qb2luKHNlbGVjdChlel9sYWJlbCwgaWQsIGN1ZV9sYWJlbCksIGJ5ID0gImlkIikgIyMgZ2V0IGN1ZSBsYWJlbCBmb3IgcGxvdHRpbmcKCiMjIHJlZmFjdG9yIHJ1ZwpzaV9ydWdfY3VlLmRmJGN1ZV9sYWJlbCA8LSBmYWN0b3Ioc2lfcnVnX2N1ZS5kZiRjdWVfbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJBc2V4dWFsIGlSQkMiLCAiU2V4dWFsIGlSQkMiLCAiVG90YWwgaVJCQyIsICJHYW1ldG9jeXRlIiwgIlJCQyIpKQoKIyMjIHNwbGl0CnNpX3J1Z19jdWUuZGZfbm9uZSA8LSBzaV9ydWdfY3VlLmRmICU+JSBmaWx0ZXIobG9nID09ICJub25lIikKc2lfcnVnX2N1ZS5kZl9sb2cgPC0gc2lfcnVnX2N1ZS5kZiAlPiUgZmlsdGVyKGxvZyA9PSAibG9nIikKYGBgCgojIyBwbG90IHJlYWN0aW9uIG5vcm1zIGFuZCBhc3NvY2lhdGVkIHJ1ZyBwbG90cwpgYGB7cn0KIyMgbm90ZSB0aGF0IGdlb21fcG9pbnQgY29udGFpbnMgYSBzZXQgb2Ygc2NyaXB0cyBhaW1lZCBhdCB0aGlubmluZyB0aGUgZGF0YSBwb2ludHMgc28gdGhleSBkbyBub3Qgb3ZlcmNyb3dkIQpmaXRuZXNzX3JuLnBsdCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBzaV9ybi5kZl9wLCBhZXMoeCA9IGN1ZV9yYW5nZSwgeSA9IGNyLCBjb2xvciA9IGxvZykpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzaV9ybi5kZl9wICU+JSAKICBtdXRhdGUocmVsX2N1ZSA9IHJvdW5kKGN1ZV9yYW5nZS8obWF4LW1pbikqMTAwKSkgJT4lIAogICAgZGlzdGluY3QoaWQsIHJlbF9jdWUsIC5rZWVwX2FsbCA9IFQpICU+JSAKICAgIGZpbHRlcihyZWxfY3VlICUlIDEwID09MSksCiAgICAgICAgICAgICBhZXMoeCA9IGN1ZV9yYW5nZSwgeSA9IGNyLCBjb2xvciA9IGxvZywgc2hhcGUgPSBsb2cpLCBzaXplID0gMikgKwogICBnZW9tX3J1ZyhkYXRhID0gc2lfcnVnX2N1ZS5kZl9ub25lLCBhZXMoeCA9IHZhbHVlKSwgY29sb3IgPSAiYmxhY2siLCBzaWRlcyA9ICJ0IiwgbGVuZ3RoID0gdW5pdCgwLjEsICJucGMiKSkgKwogIGdlb21fcnVnKGRhdGEgPSBzaV9ydWdfY3VlLmRmX2xvZywgYWVzKHggPSB2YWx1ZSksIGNvbG9yID0gIiNENDExNTkiLCBzaWRlcyA9ICJiIiwgbGVuZ3RoID0gdW5pdCgwLjEsICJucGMiKSkgKwogIGZhY2V0X3dyYXAofmN1ZV9sYWJlbCwgc2NhbGVzID0gImZyZWVfeCIsIG5jb2wgPSAxKSArCiAgbGFicyh4ID0gIkN1ZSByYW5nZSIsIHkgPSAiVHJhbnNtaXNzaW9uIGludmVzdG1lbnQiLCBjb2xvciA9ICJDdWUgc3RhdHVzIiwgc2hhcGUgPSAiQ3VlIHN0YXR1cyIpICsKICB5bGltKDAsIDEpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiNENDExNTkiLCAiYmxhY2siKSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBmb3JtYXQoeCwgc2NpZW50aWZpYyA9IFQpLAogICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2F4aXMoY2hlY2sub3ZlcmxhcCA9IFRSVUUpKSArIAogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMtLS0tLS0tLS0tLS0gUGxvdHRpbmcgZml0bmVzcywgZHluYW1pY3MsIGFuZCBybiB0b2dldGhlciAtLS0tLS0tLS0tLS0jCmBgYHtyfQpnZ2FycmFuZ2UoZml0bmVzc19yYW5rX2NyLnBsdCwgZml0bmVzc19ybi5wbHQsIHdpZHRocyA9IGMoMi43LCAxKSwgYWxpZ24gPSAiaCIsIG5jb2wgPSAyLCBsYWJlbHMgPSBjKCJBIiwgIkIiKSkKZ2dzYXZlKHVuaXRzID0gInB4IiwgZHBpID0gMzAwLCB3aWR0aCA9IDIyNTAsIGhlaWdodCA9IDE1MDAsIGZpbGVuYW1lID0gaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvZml0bmVzc19ybi50aWZmIiksIGJnID0gIndoaXRlIiwgc2NhbGUgPSAxLjI1KQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgRHVhbCBjdWUgZml0bmVzcyBtYWluIGZpZ3VyZQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojLS0tLS0tLS0tLS0tUmVsYXRpdmUgcmFua2luZyBvZiBkdWFsIGN1ZSB2cyBzaW5nbGUgY3VlIGZpdG5lc3MtLS0tLS0tLS0tLSMKIyMgUHJlcGFyZSBkdWFsIGN1ZSBkYXRhc2V0CmBgYHtyfQojIyBGb3IgdGhlIGR1YWwgY3VlIG1vZGVscywgd2Ugb3B0aW1pemVkIGVhY2ggY3VlIGNvbWJpbmF0aW9ucyBpbiAyIHdheXMsIG9uZSB3aXRoIEwtQkZHUy1CIHdpdGggMC41WDkgc3RhcnRpbmcgcG9pbnQgYW5kIGFub3RoZXIgd2l0aCBERW9wdGltICsgTC1GR0JTLUIuIEZvciBlYWNoIGN1ZSBjb21iaW5hdGlvbiwgd2UgYXJlIGdvaW5nIHRvIHBpY2sgdGhlIHN0cmF0ZWd5IHRoYXQgZ2F2ZSB1cyB0aGUgaGlnaGVyIGZpdG5lc3MKZHVhbF9jdWVfZl9maW5hbC5kZiA8LSBkdWFsX2N1ZV9mX2dsYi5kZiAlPiUgCiAgc2VsZWN0KGlkLCBpZF9iLCBsYWJlbCwgbGFiZWxfYiwgZml0bmVzc19nbGIgPSBmaXRuZXNzLCAKICAgICAgICAgcGFyMV9nbGIgPSBwYXIxLCBwYXIyX2dsYiA9IHBhcjIsIHBhcjNfZ2xiID0gcGFyMywgcGFyNF9nbGIgPSBwYXI0LCBwYXI1X2dsYiA9IHBhcjUsIHBhcjZfZ2xiID0gcGFyNiwKICAgICAgICAgcGFyN19nbGIgPSBwYXI3LCBwYXI4X2dsYiA9IHBhcjgsIHBhcjlfZ2xiID0gcGFyOSkgJT4lIAogIGxlZnRfam9pbigKICAgIHNlbGVjdChkdWFsX2N1ZV9mX2xjLmRmLAogICAgICAgICBpZCwgaWRfYiwgZml0bmVzc19sYyA9IGZpdG5lc3MsIAogICAgICAgICBwYXIxX2xjID0gcGFyMSwgcGFyMl9sYyA9IHBhcjIsIHBhcjNfbGMgPSBwYXIzLCBwYXI0X2xjID0gcGFyNCwgcGFyNV9sYyA9IHBhcjUsIHBhcjZfbGMgPSBwYXI2LAogICAgICAgICBwYXI3X2xjID0gcGFyNywgcGFyOF9sYyA9IHBhcjgsIHBhcjlfbGMgPSBwYXI5KSwgYnkgPSBjKCJpZCIsICJpZF9iIikKICApICU+JSAjIyByZW5hbWUgY29sdW1ucyBhbmQgam9pbiBnbG9iYWwgYW5kIGxvY2FsIG9wdGltaXphdGlvbiBmaXRuZXNzIGRhdGFmcmFtZXMKICBtdXRhdGUoCiAgICBmaXRuZXNzID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgZml0bmVzc19nbGIsIGZpdG5lc3NfbGMpLAogICAgcGFyMSA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIHBhcjFfZ2xiLCBwYXIxX2xjKSwKICAgIHBhcjIgPSBpZmVsc2UoZml0bmVzc19nbGIgPiBmaXRuZXNzX2xjLCBwYXIyX2dsYiwgcGFyMl9sYyksCiAgICBwYXIzID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgcGFyM19nbGIsIHBhcjNfbGMpLAogICAgcGFyNCA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIHBhcjRfZ2xiLCBwYXI0X2xjKSwKICAgIHBhcjUgPSBpZmVsc2UoZml0bmVzc19nbGIgPiBmaXRuZXNzX2xjLCBwYXI1X2dsYiwgcGFyNV9sYyksCiAgICBwYXI2ID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgcGFyNl9nbGIsIHBhcjZfbGMpLAogICAgcGFyNyA9IGlmZWxzZShmaXRuZXNzX2dsYiA+IGZpdG5lc3NfbGMsIHBhcjdfZ2xiLCBwYXI3X2xjKSwKICAgIHBhcjggPSBpZmVsc2UoZml0bmVzc19nbGIgPiBmaXRuZXNzX2xjLCBwYXI4X2dsYiwgcGFyOF9sYyksCiAgICBwYXI5ID0gaWZlbHNlKGZpdG5lc3NfZ2xiID4gZml0bmVzc19sYywgcGFyOV9nbGIsIHBhcjlfbGMpCiAgKSAjIyB0aGUgY29sdW1ucyBhcmUgYXNzaWduZWQgYmFzZWQgb24gd2hpY2ggc3RyYXRlZ3kgcHJvZHVjZWQgdGhlIGhpZ2hlc3QgZml0bmVzcwoKIyMgd3JpdGUgY3N2CndyaXRlLmNzdihkdWFsX2N1ZV9mX2ZpbmFsLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9maXRuZXNzX2ZpbmFsLmNzdiIpKQpgYGAKCiMjIENvbWJpbmUgc2luZ2xlIGN1ZSBhbmQgZHVhbCBjdWUgZGF0YXNldApgYGB7cn0KZHVhbF9jdWVfZl9maW5hbC5kZiA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9maXRuZXNzX2ZpbmFsLmNzdiIpKQoKZHVhbF9zaV9maXRuZXNzLmRmIDwtIGR1YWxfY3VlX2ZfZmluYWwuZGYgJT4lIAogIHNlbGVjdChpZCwgaWRfYiwgbGFiZWwsIGxhYmVsX2IsIGZpdG5lc3NfZHVhbCA9IGZpdG5lc3MpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KHNpX29wdC5kZiwgaWQsIGZpdG5lc3Nfc2kgPSBmaXRuZXNzXzIwKSwgYnkgPSAiaWQiKSAlPiUgIyMgZ2V0IGZpdG5lc3Mgb2Ygc2luZ2xlIGN1ZSBtb2RlbCBiYXNlZCBvbiBmaXJzdCBjdWUKICBsZWZ0X2pvaW4oc2VsZWN0KHNpX29wdC5kZiwgaWRfYiA9IGlkLCBmaXRuZXNzX3NpX2IgPSBmaXRuZXNzXzIwKSwgYnkgPSAiaWRfYiIpICU+JSAgIyMgZ2V0IGZpdG5lc3Mgb2Ygc2luZ2xlIGN1ZSBtb2RlbCBiYXNlZCBvbiBzZWNvbmQgY3VlCiAgbXV0YXRlKGZpdG5lc3Nfc2lfZmluYWwgPSBpZmVsc2UoZml0bmVzc19zaSA+IGZpdG5lc3Nfc2lfYiwgZml0bmVzc19zaSwgZml0bmVzc19zaV9iKS85Ljg4MzYwMiwKICAgICAgICAgbGFiZWxfY29tYiA9IGdzdWIoIklcXCtJZyIsICJUb3RhbCBJIiwgcGFzdGUobGFiZWwsICImIiwgbGFiZWxfYikpLCAjIyBzZWxlY3QgdGhlIGhpZ2hlc3Qgc2luZ2xlIGN1ZSBtb2RlbCBmaXRuZXNzCiAgICAgICAgIGZpdG5lc3NfZHVhbF9ub3JtID0gZml0bmVzc19kdWFsLzkuODgzNjAyKSMjIG5vcm1hbGl6ZSBieSB0aW1lIGZpdG5lc3MKYGBgCgojIyBwbG90IGZpdG5lc3MgcmFuawpgYGB7cn0KZHVhbF9maXRuZXNzLnBsIDwtIGdncGxvdChkdWFsX3NpX2ZpdG5lc3MuZGYpICsKICBnZW9tX3NlZ21lbnQoYWVzKHkgPSBmY3RfcmVvcmRlcihsYWJlbF9jb21iLCBmaXRuZXNzX2R1YWxfbm9ybSksIHllbmQgPSBmY3RfcmVvcmRlcihsYWJlbF9jb21iLCBmaXRuZXNzX2R1YWwpLCB4ID0gZml0bmVzc19kdWFsX25vcm0sIHhlbmQgPSBmaXRuZXNzX3NpX2ZpbmFsKSkgKyAjIyBsaW5lIHNlZ21lbnQgY29ubmVjdGluZyB0aGUgY3VlIHZhbHVlcwogIGdlb21fcG9pbnQoYWVzKHkgPSBmY3RfcmVvcmRlcihsYWJlbF9jb21iLCBmaXRuZXNzX2R1YWwpLCB4ID0gZml0bmVzc19kdWFsX25vcm0sIGNvbG9yID0gIkR1YWwgY3VlIiwgc2hhcGUgPSAiRHVhbCBjdWUiKSwKICAgICAgICAgICAgIHNpemUgPSAyLjUpICsgIyMgZHVhbCBjdWUgZml0bmVzcwogIGdlb21fcG9pbnQoYWVzKHkgPSBmY3RfcmVvcmRlcihsYWJlbF9jb21iLCBmaXRuZXNzX2R1YWxfbm9ybSksIHggPSBmaXRuZXNzX3NpX2ZpbmFsLCBjb2xvciA9ICJCZXN0IHNpbmdsZSBjdWUiLCBzaGFwZSA9ICJCZXN0IHNpbmdsZSBjdWUiKSwgc2l6ZSA9IDIuNSkgKyAjIyBiZXN0IHNpbmdsZSBjdWUgZml0bmVzcwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiI2ZjOGQ1OSIsICIjNDU3NWI0IikpICsKICBsYWJzKHggPSAiTm9ybWFsaXplZCBmaXRuZXNzIiwgeSA9ICJEdWFsIGN1ZSBjb21iaW5hdGlvbnMiLCBjb2xvdXIgPSAiTGVnZW5kIiwgc2hhcGUgPSAiTGVnZW5kIikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCmBgYAoKIy0tLS0tLS0tLS0tLVRpbWUgc2VyaWVzIGNvbnZlcnNpb24gcmF0ZSBvZiBkdWFsIGN1ZSBtb2RlbHMtLS0tLS0tLS0tLSMKTm90ZSB0aGF0IG91ciBkdWFsIGN1ZSBtb2RlbHMgdGFrZSA5IHBhcmFtZXRlcnMuIFRvIGVuc3VyZSB0aGF0IGFueSBkaWZmZXJlbmNlICBiZXR3ZWVuIGR1YWwgYW5kIHNpbmdsZSBjdWUgbW9kZWxzIGlzIGR1ZSB0byB0aGUgaW5jbHVzaW9uIG9mIGFkZGl0aW9uYWwgY3VlcyBhbmQgTk9UIGhpZ2hlciBzcGxpbmUgZmxleGliaWxpdHksd2UgcGVyZm9ybWVkIEwtQkZHUy1CIChsb2NhbCkgb3B0aW1pemF0aW9uIG9mIHNpbmdsZSBjdWUgbW9kZWxzIHdpdGggZGYgPSA5IHRvIGNvbnRyb2wgZm9yIHRoZSBzcGxpbmUgZmxleGliaWxpdHkuIAoKIyMgZHluYW1pY3Mgc2ltdWxhdGlvbiBvZiBoaWdoIHBhcmFtZXRlciBjdWVzIApgYGB7cn0KIyMgYmVzdCBkdWFsIGN1ZSBtb2RlbDogSSBsb2cgYW5kIFIgbG9nClJsb2dfSWxvZy5jciA8LSBjaGFiYXVkaV9zaV9jbGVhbigKICBwYXJhbWV0ZXJzX2NyID0gYyg0LjQ0NjE5MjAzMywJMTAuOTc1MTgyNzUsCTEuMzg3NjI4MTcsCTIzLjMwNTkyNTQsCS0zLjQ1MjA1MjM3MSwJLTE4LjAwNzA2OTIsCTM5LjY2NjE0MjI2LAktMy41NDUxOTMxNDEsCTE4Ljc4MzUwNzk5KSwKICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGksCiAgdGltZV9yYW5nZSA9IHNlcSgwLCAyMCwgYnkgPSAxZS0zKSwKICBjdWVfcmFuZ2UgPSAgc2VxKDYsIDcsIGJ5ID0gMS81MDApLAogIGN1ZV9yYW5nZV9iID0gc2VxKDAsIGxvZzEwKDYqKDEwXjYpKSwgYnkgPSAobG9nMTAoNiooMTBeNikpKS81MDApLAogIGN1ZSA9ICJSIiwKICBjdWVfYiA9ICJJIiwKICBsb2dfY3VlID0gImxvZzEwIiwKICBsb2dfY3VlX2IgPSAibG9nMTAiLAogIHNvbHZlciA9ICJ2b2RlIiwKICBkeW4gPSBUCikKCiMjIHdoZW4gdGltZSBpcyB1c2VkIGFzIGEgY3VlICg5IHBhcmFtZXRlcnMpLiBjaGFiYXVkaV9zaV9jbGVhbl9oaWdoIGlzIHNpbXBseSBhIHZhcmlhdGlvbiBvZiBjaGFiYXVkaV9zaV9jbGVhbiB0aGF0IG1ha2VzIGEgbW9yZSBmbGV4aWJsZSBzcGxpbmUhCnRpbWVfaGlnaC5jciA8LSBjaGFiYXVkaV9zaV9jbGVhbl9oaWdoKAogIHBhcmFtZXRlcnNfY3IgPSBjKDkuMTU0MzE0LCAgLTcuNTcwODI5LCAtMjIuNTA2NjM4ICwgIDMuMzgyNDA1ICwtMTMuNDUzNTE5ICwtMTcuMDExNDg1ICAsIDMuNjc4MTgxLCAtMTIuODUxODk1ICwtMjYuMTE1MTU4KSwKICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGksCiAgdGltZV9yYW5nZSA9IHNlcSgwLCAyMCwgYnkgPSAxZS0zKSwKICBjdWVfcmFuZ2UgPSAgc2VxKDAsIDIwLCBieSA9IDFlLTMpLAogIGN1ZSA9ICJ0IiwKICBzb2x2ZXIgPSAidm9kZSIsCiAgZHluID0gVCkKCiMjIHdoZW4gYXNleHVhbCBpUkJDIGlzIHVzZWQgYXMgYSBjdWUgKGhpZ2ggZmxleGliaWxpdHkpCklfaGlnaC5jciA8LSBjaGFiYXVkaV9zaV9jbGVhbl9oaWdoKAogIHBhcmFtZXRlcnNfY3IgPSBjKDEuMjk2Njc1LCAgMy41NDQwMzQgLCA0LjkwNzQ4NCwgIDIuMTc0MjQ5LCAtMy4yMzgzMDkgLC01LjE4MTYxNCAsLTEuNjQ1MDcyICwgMS44MzQzMDIgLCAxLjU4MTAxMSksCiAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogIHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXJzX3RzdWt1c2hpLAogIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIGJ5ID0gMWUtMyksCiAgY3VlX3JhbmdlID0gIHNlcSgwLCBsb2cxMCg2KigxMF42KSksIGJ5ID0gKGxvZzEwKDYqKDEwXjYpKSkvNTAwMCksCiAgY3VlID0gIkkiLAogIGxvZ19jdWUgPSAibG9nMTAiLAogIHNvbHZlciA9ICJ2b2RlIiwKICBkeW4gPSBUKQoKIyMgd2hlbiBSQkMgaXMgdXNlZCBhcyBjdWUgKGhpZ2ggZmxleGliaWxpdHkpClJfaGlnaC5jciA8LSBjaGFiYXVkaV9zaV9jbGVhbl9oaWdoKAogIHBhcmFtZXRlcnNfY3IgPSBjKDUuMDM0MDM0OCAsICAwLjU4NDYxNjggLCAgMC4zNzQ5NjQ4ICwgIDAuNjg0MjY3MyAgLCAyLjQ3NDgxMDcgLCAxMC45MDM2MDM0ICwgMTYuODI0NjMxNiwgLTI0Ljg2OTA5NzEgLDEuODAwNzIzOCksCiAgaW1tdW5pdHkgPSAidHN1a3VzaGkiLAogIHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXJzX3RzdWt1c2hpLAogIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIGJ5ID0gMWUtMyksCiAgY3VlX3JhbmdlID0gIHNlcShsb2cxMCgxMF42KSwgbG9nMTAoMTBeNyksIGJ5ID0gKGxvZzEwKDEwXjcpLWxvZzEwKDEwXjYpKS81MDAwKSwKICBjdWUgPSAiUiIsCiAgbG9nX2N1ZSA9ICJsb2cxMCIsCiAgc29sdmVyID0gInZvZGUiLAogIGR5biA9IFQpCgojIHByb2Nlc3MgCklfaGlnaC5jcl9wIDwtIElfaGlnaC5jciAlPiUgZmlsdGVyKHZhcmlhYmxlID09ICJjciIpICU+JSBtdXRhdGUobGFiZWxfbmV3ID0gIkkgbG9nIChkZj05KSIpICU+JSBzZWxlY3QoLXZhcmlhYmxlKQoKUl9oaWdoLmNyX3AgPC0gUl9oaWdoLmNyICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gImNyIikgJT4lIG11dGF0ZShsYWJlbF9uZXcgPSAiUiBsb2cgKGRmPTkpIikgJT4lIHNlbGVjdCgtdmFyaWFibGUpCgp0aW1lX2hpZ2guY3JfcCA8LSB0aW1lX2hpZ2guY3IgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiY3IiKSAlPiUgbXV0YXRlKGxhYmVsX25ldyA9ICJUaW1lIChkZj05KSIpICU+JSBzZWxlY3QoLXZhcmlhYmxlKQoKUmxvZ19JbG9nLmNyX3AgPC0gUmxvZ19JbG9nLmNyICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gImNyIikgJT4lIG11dGF0ZShsYWJlbF9uZXcgPSAiUiBsb2cgJiBJIGxvZ1xuKGRmPTkpIikgJT4lIHNlbGVjdCgtdmFyaWFibGUpCgojIyBjb21iaW5lCmR1YWxfc2VsZWN0ZWRfY3IuZGYgPC0gcmJpbmQoSV9oaWdoLmNyX3AsIFJfaGlnaC5jcl9wLCB0aW1lX2hpZ2guY3JfcCwgUmxvZ19JbG9nLmNyX3ApCndyaXRlX3BhcnF1ZXQoZHVhbF9zZWxlY3RlZF9jci5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9zZWxlY3RlZF9jci5wYXJxdWV0IikpCmBgYAoKIyMgcGxvdApgYGB7cn0KZHVhbF9zZWxlY3RlZF9jci5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9zZWxlY3RlZF9jci5wYXJxdWV0IikpCgpkdWFsX3NlbGVjdGVkX2NyLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGR1YWxfc2VsZWN0ZWRfY3IuZGYsIGFlcyhjb2xvciA9IGxhYmVsX25ldywgeCA9IHRpbWUsIHkgPSB2YWx1ZSksIHNpemUgPSAxKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZHVhbF9zZWxlY3RlZF9jci5kZiAlPiUgZmlsdGVyKHRpbWUlJTEgPT0gMCksIGFlcyhjb2xvciA9IGxhYmVsX25ldywgeCA9IHRpbWUsIHkgPSB2YWx1ZSwgc2hhcGUgPSBsYWJlbF9uZXcpLCBzaXplID0gMykgKwogIGxhYnMoeCA9ICJUaW1lIChkYXlzKSIsIHkgPSAiVHJhbnNtaXNzaW9uIGludmVzdG1lbnQiLCBjb2xvciA9ICJDdWUocykiLCBzaGFwZSA9ICJDdWUocykiKSArCiAgeGxpbSgwLCAyMCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKG9yYW5nZSwiI0UxQkU2QSIsYmx1ZSAsImJsYWNrIikpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKHQgPSA0MCwgciA9IDAsIGIgPSAwLCBsID0gMCwgdW5pdCA9ICJwdCIpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSA0LCBieXJvdyA9IFRSVUUpKQpgYGAKCiMtLS0tLS0tLSBSZWFjdGlvbiBub3JtIGhlYXRtYXAgb2YgUiBsb2cxMCArIEkgbG9nMTAgLS0tLS0tLS0tLS0tIwojIFByb2Nlc3MgZGF0YQpgYGB7cn0KIyBtYWtlIGhlYXRtYXAgZGF0YS5mcmFtZQpSbG9nX0lsb2cuaG0gPC0gcGFyX3RvX2htX3RlKHBhciA9IGMoNC40NDYxOTIwMzMsCTEwLjk3NTE4Mjc1LAkxLjM4NzYyODE3LAkyMy4zMDU5MjU0LAktMy40NTIwNTIzNzEsCS0xOC4wMDcwNjkyLAkzOS42NjYxNDIyNiwJLTMuNTQ1MTkzMTQxLAkxOC43ODM1MDc5OSksCiAgICAgICAgICAgICBjdWVfcmFuZ2UgPSBzZXEoNiwJNywgbGVuZ3RoLm91dCA9IDUwMCksCiAgICAgICAgICAgICBjdWVfcmFuZ2VfYiA9IHNlcSgwLAk2Ljc3ODE1MTI1LCBsZW5ndGgub3V0ID0gNTAwKSkKCiMgcHJvY2VzcyBkeW5hbWljcwpSbG9nX0lsb2cuZHluIDwtIFJsb2dfSWxvZy5jciAlPiUgCiAgdGlkeXI6OnBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB2YXJpYWJsZSwgdmFsdWVzX2Zyb20gPSB2YWx1ZSkgJT4lIAogIG11dGF0ZShsb2dfUiA9IGxvZzEwKFIpLAogICAgICAgICBsb2dfSSA9IGxvZzEwKEkpKQpgYGAKCiMgcGxvdApgYGB7cn0KUmxvZ19JbG9nX3JuLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX3Jhc3RlcihkYXRhID0gUmxvZ19JbG9nLmhtLCBhZXMoeCA9IGN1ZV9yYW5nZV9iLCB5ID0gY3VlX3JhbmdlLCBmaWxsID0gY3IpKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSBSbG9nX0lsb2cuZHluLCBhZXMoeCA9IGxvZ19JLCB5ID0gbG9nX1IpLCBjb2xvciA9ICJ3aGl0ZSIsIGFycm93ID0gYXJyb3coYW5nbGUgPSAzMCwgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSkpICsKICBnZW9tX3BvaW50KGRhdGEgPSBSbG9nX0lsb2cuZHluICU+JSBmaWx0ZXIocm93X251bWJlcigpICUlIDEwMDAgPT0gMSAmIHRpbWUgPD0gMjApLCBhZXMoeCA9IGxvZ19JLCB5ID0gbG9nX1IpLCBjb2xvciA9ICJ3aGl0ZSIpICsKICB4bGltKDAuOTkqbWluKGhhYmxhcjo6cyhSbG9nX0lsb2cuZHluJGxvZ19JKSwgbmEucm0gPSBUKSwgMS4wMSogbWF4KGhhYmxhcjo6cyhSbG9nX0lsb2cuZHluJGxvZ19JKSwgbmEucm0gPSBUKSkgKwogIHlsaW0oMC45OSptaW4oaGFibGFyOjpzKFJsb2dfSWxvZy5keW4kbG9nX1IpLCBuYS5ybSA9IFQpLDEuMDEqIG1heChoYWJsYXI6OnMoUmxvZ19JbG9nLmR5biRsb2dfUiksIG5hLnJtID0gVCkpICsKICBsYWJzKHkgPSAiUkJDIGxvZyIsIHggPSAiQXNleHVhbCBpUkJDIGxvZyIsIGZpbGwgPSAiVHJhbnNtaXNzaW9uXG5pbnZlc3RtZW50IikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKYGBgCgojLS0tLS0tLS0tLS0gUGxvdCB0b2dldGhlciAtLS0tLS0tLS0tLS0jCmBgYHtyfQojIGFzc2VtYmxlIHBhbmVsIEIgYW5kIEMKZHVhbF9tYWluLkJDIDwtIGdnYXJyYW5nZShkdWFsX3NlbGVjdGVkX2NyLnBsLCBSbG9nX0lsb2dfcm4ucGwsIGFsaWduID0gInYiLCBuY29sID0gMSwgbGFiZWxzID0gYygiQiIsICJDIikpCgojIGFzc2VtYmxlIHBhbmVsIEEKZ2dhcnJhbmdlKGR1YWxfZml0bmVzcy5wbCwgZHVhbF9tYWluLkJDLCBuY29sID0gMiwgbGFiZWxzID0gYygiQSIsICIiKSwgd2lkdGhzID0gYygxLjEsMSkpCmdnc2F2ZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9kdWFsX21haW5faW50ZXJtZWRpYXRlLnRpZmYiKSwgdW5pdHMgPSAicHgiLCB3aWR0aCA9IDIyNTAsIGhlaWdodCA9IDE0MDAsIHNjYWxlID0gMS4zLCBkcGk9MzAwLCAgYmcgPSAid2hpdGUiKQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgRHVhbCBjdWUgY29udmVyc2lvbiByYXRlIHN1cHBsZW1lbnRhcnkgZmlndXJlIChjciBkeW5hbWljcykKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyMgRnVuY3Rpb24gdG8gc2ltdWxhdGUgZHVhbCBjdWUgZHluYW1pY3MKYGBge3J9CmR1YWxfY3VlX2R5biA8LSBmdW5jdGlvbihkZil7CiAgIyMgcHJvY2VzcyBjdWVzCiAgY3VlIDwtIGRmJGN1ZQogIGN1ZV9iIDwtIGRmJGN1ZV9iCiAgCiAgIyMgcHJvY2VzcyBsb2cKICBsb2cgPC0gaWZlbHNlKHN0cl9kZXRlY3QoZGYkaWQsICJsb2ciKSwgImxvZzEwIiwgIm5vbmUiKQogIGxvZ19iIDwtIGlmZWxzZShzdHJfZGV0ZWN0KGRmJGlkX2IsICJsb2ciKSwgImxvZzEwIiwgIm5vbmUiKQogIAogICMgcHJvY2VzcyBjdWVfcmFuZ2UuIGVuc3VyZSB0aGF0IGJvdGggY3VlIHJhbmdlcyBhcmUgb2YgdGhlIHNhbWUgbGVuZ3RoCiAgY3VlX3JhbmdlIDwtIHNlcShkZiRsb3csIGRmJGhpZ2gsIGxlbmd0aC5vdXQgPSA1MDApCiAgY3VlX3JhbmdlX2IgPC0gc2VxKGRmJGxvd19iLCBkZiRoaWdoX2IsIGxlbmd0aC5vdXQgPSA1MDApCiAgCiAgIyBnZXQgcGFyYW1ldGVyIHNldAogIHBhciA8LSBjKGRmJHBhcjEsIGRmJHBhcjIsIGRmJHBhcjMsIGRmJHBhcjQsIGRmJHBhcjUsIGRmJHBhcjYsIGRmJHBhcjcsIGRmJHBhcjgsIGRmJHBhcjkpCiAgCiAgIyBzaW11bGF0ZSBkeW5hbWljcwogIGR5biA8LSBjaGFiYXVkaV9zaV9jbGVhbigKICAgIHBhcmFtZXRlcnNfY3IgPSBwYXIsCiAgICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgICBwYXJhbWV0ZXJzID0gcGFyYW1ldGVyc190c3VrdXNoaSwKICAgIHRpbWVfcmFuZ2UgPSBzZXEoMCwgMjAsIDAuMDEpLAogICAgY3VlID0gY3VlLAogICAgY3VlX2IgPSBjdWVfYiwKICAgIGN1ZV9yYW5nZSA9IGN1ZV9yYW5nZSwKICAgIGN1ZV9yYW5nZV9iID0gY3VlX3JhbmdlX2IsCiAgICBsb2dfY3VlID0gbG9nLAogICAgbG9nX2N1ZV9iID0gbG9nX2IsCiAgICBzb2x2ZXIgPSAidm9kZSIsCiAgICBnYW0gPSAidGUiLAogICAgZHluID0gVCkKICAKICAjIHJldHVybiAKICBkeW4yIDwtIGNiaW5kKGlkID0gZGYkaWQsIGlkX2IgPSBkZiRpZF9iLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gZGYkbGFiZWwsIGxhYmVsX2IgPSBkZiRsYWJlbF9iLAogICAgICAgICAgICAgICAgY3VlID0gY3VlLCBjdWVfYiA9IGN1ZV9iLCBkeW4pCiAgCiAgd3JpdGVfcGFycXVldChkeW4yLCBoZXJlKHBhc3RlMCgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZHluLyIsIGRmJGlkLCAiXyIsIGRmJGlkX2IsICIucGFycXVldCIpKSkKfQpgYGAKCiMjIEdldCBkeW5hbWljcyBvZiBkdWFsIGN1ZSBtb2RlbHMKYGBge3J9CiMjIEZvciB0aGUgZHVhbCBjdWUgZml0bmVzcyBkYXRhZnJhbWUsIGFkZCB0aGUgY3VlIHJhbmdlIGZvciBlYWNoIGluZGl2aWR1YWwgY3VlCmR1YWxfY3VlX2ZfZmluYWwuZGZfcCA8LSBkdWFsX2N1ZV9mX2ZpbmFsLmRmICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGN1ZV9yYW5nZV9zaV9hbHQuZGYsIGlkLCBjdWUsIGxvdywgaGlnaCksIGJ5ID0gImlkIikgJT4lIAogIGxlZnRfam9pbihzZWxlY3QoY3VlX3JhbmdlX3NpX2FsdC5kZiwgaWRfYiA9IGlkLCBjdWVfYiA9IGN1ZSwgbG93X2IgPSBsb3csIGhpZ2hfYiA9IGhpZ2gpLCBieSA9ICJpZF9iIikKCiMjIFNwbGl0IGRhdGFmcmFtZXMKZHVhbF9jdWVfZl9maW5hbC5scyA8LSBzcGxpdChkdWFsX2N1ZV9mX2ZpbmFsLmRmX3AsIHNlcShucm93KGR1YWxfY3VlX2ZfZmluYWwuZGZfcCkpKQoKIyMgUnVuIGZ1bmN0aW9uCm1jbGFwcGx5KGR1YWxfY3VlX2ZfZmluYWwubHMsIGR1YWxfY3VlX2R5biwgbWMuY29yZXMgPSA2KQoKIyMgQ29uY2F0IGFsbCBmaWxlcwpkdWFsX2N1ZV9keW4ubHMgPC0gbGlzdC5maWxlcyhwYXRoID0gaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZHluIiksIHBhdHRlcm4gPSAiKi5wYXJxdWV0IiwgZnVsbC5uYW1lcyA9IFQpCmR1YWxfY3VlX2R5bi5kZiA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoZHVhbF9jdWVfZHluLmxzLCByZWFkX3BhcnF1ZXQpKQp3cml0ZV9wYXJxdWV0KGR1YWxfY3VlX2R5bi5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9jdWVfZHluLnBhcnF1ZXQiKSkKYGBgCgojIyBnZXQgMzAgZGF5cyBkeW5hbWljIChmb3IgcGxvdHRpbmcgcHVycG9zZXMpCmBgYHtyfQojIyBSdW4gZnVuY3Rpb24KbWNsYXBwbHkoZHVhbF9jdWVfZl9maW5hbC5scywgZHVhbF9jdWVfZHluLCBtYy5jb3JlcyA9IDYpCgojIyBDb25jYXQgYWxsIGZpbGVzCmR1YWxfY3VlX2R5bl8zMC5scyA8LSBsaXN0LmZpbGVzKHBhdGggPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW5fMzAiKSwgcGF0dGVybiA9ICIqLnBhcnF1ZXQiLCBmdWxsLm5hbWVzID0gVCkKZHVhbF9jdWVfZHluXzMwLmRmIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShkdWFsX2N1ZV9keW5fMzAubHMsIHJlYWRfcGFycXVldCkpCndyaXRlX3BhcnF1ZXQoZHVhbF9jdWVfZHluXzMwLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX2N1ZV9keW5fMzAucGFycXVldCIpKQpgYGAKCiMjIFByZXBhcmluZyBkYXRhc2V0IGZvciBwbG90dGluZwpgYGB7cn0KIyMgZmlsdGVyIG91dCBvbmx5IGNvbnZlcnNpb24gcmF0ZSBhbmQgYXR0YWNoIGZpdG5lc3MgdmFsdWVzCmR1YWxfY3VlX2NyLmRmIDwtIGR1YWxfY3VlX2R5bi5kZiAlPiUgCiAgZmlsdGVyKHZhcmlhYmxlID09ICJjciIpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGR1YWxfY3VlX2ZfZmluYWwuZGYsIGlkLCBpZF9iLCBmaXRuZXNzKSwgYnkgPSBjKCJpZCIsICJpZF9iIikpICU+JSAKICBtdXRhdGUobGFiZWxfY29tYiA9IGdzdWIoIklcXCtJZyIsICJUb3RhbCBJIiwgcGFzdGUobGFiZWwsICImIiwgbGFiZWxfYikpKQoKIyMgU2FuaXR5IGNoZWNrIHRoYXQgdGhlIGR5bmFtaWNzIHByb2R1Y2VkIHRoZSBzYW1lIGZpdG5lc3MgYXMgdGhlIG9wdGltaXplZCB2YWx1ZXMuIFllcyEKZHVhbF9jdWVfZHluLmRmICU+JSAKICBmaWx0ZXIodmFyaWFibGUgPT0gInRhdV9jdW0iKSAlPiUgCiAgZmlsdGVyKHRpbWUgPT0gMjApICU+JSAKICBkaXN0aW5jdChpZCwgaWRfYiwgdmFsdWUpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KGR1YWxfY3VlX2ZfZmluYWwuZGYsIGlkLCBpZF9iLCBmaXRuZXNzKSwgYnkgPSBjKCJpZCIsICJpZF9iIikpICU+JSAKICBtdXRhdGUoZGlmZiA9IHZhbHVlLWZpdG5lc3MpCmBgYAoKIyMgUGxvdApgYGB7cn0KZ2dwbG90KGRhdGEgPSBkdWFsX2N1ZV9jci5kZiwgYWVzKHggPSB0aW1lLCB5ID0gZmN0X3Jlb3JkZXIobGFiZWxfY29tYiwgZml0bmVzcyksIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3Jhc3RlcigpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsKICB4bGltKDEsIDIwKSArCiAgbGFicyh4ID0gIlRpbWUgKGRheXMpIiwgeSA9ICJEdWFsIGN1ZSBjb21iaW5hdGlvbiIsIGZpbGwgPSAiVHJhbnNtaXNzaW9uXG5pbnZlc3RtZW50IikgKwogIHRoZW1lX2NsYXNzaWMoKQoKZ2dzYXZlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL2R1YWxfY3VlX2NyLnRpZmYiKSwgdW5pdHMgPSAicHgiLCB3aWR0aCA9IDIwMDAsIGhlaWdodCA9IDE1MDAsIGRwaT0zMDAsICBiZyA9ICJ3aGl0ZSIpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBDdW11bGF0aXZlIHRyYW5zbWlzc2lvbiBwbG90IG9mIGR1YWwgYW5kIHNpbmdsZSBjdWUgbW9kZWxzCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgZ2V0IGN1bXVsYXRpdmUgdGF1CmBgYHtyfQojIGZpbHRlciBhbmQgYWRkIG5hbWVzCklfaGlnaC50YXVjdW0gPC0gSV9oaWdoLmNyICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gInRhdV9jdW0iKSAlPiUgbXV0YXRlKGxhYmVsX25ldyA9ICJJIGxvZyAoZGY9OSkiKSAlPiUgc2VsZWN0KC12YXJpYWJsZSkKClJfaGlnaC50YXVjdW0gPC0gUl9oaWdoLmNyICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gInRhdV9jdW0iKSAlPiUgbXV0YXRlKGxhYmVsX25ldyA9ICJSIGxvZyAoZGY9OSkiKSAlPiUgc2VsZWN0KC12YXJpYWJsZSkKCnRpbWVfaGlnaC50YXVjdW0gPC0gdGltZV9oaWdoLmNyICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gInRhdV9jdW0iKSAlPiUgbXV0YXRlKGxhYmVsX25ldyA9ICJUaW1lIChkZj05KSIpICU+JSBzZWxlY3QoLXZhcmlhYmxlKQoKUmxvZ19JbG9nLnRhdWN1bSA8LSBSbG9nX0lsb2cuY3IgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAidGF1X2N1bSIpICU+JSBtdXRhdGUobGFiZWxfbmV3ID0gIlIgbG9nICYgSSBsb2dcbihkZj05KSIpICU+JSBzZWxlY3QoLXZhcmlhYmxlKQoKCiMgYmluZCB0b2dldGhlcgpkdWFsX3NlbGVjdGVkX3RhdWN1bS5kZiA8LSByYmluZChJX2hpZ2gudGF1Y3VtLCBSX2hpZ2gudGF1Y3VtLCB0aW1lX2hpZ2gudGF1Y3VtLCBSbG9nX0lsb2cudGF1Y3VtKQoKIyB3cml0ZQp3cml0ZV9wYXJxdWV0KGR1YWxfc2VsZWN0ZWRfdGF1Y3VtLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9kdWFsX3NlbGVjdGVkX3RhdWN1bS5wYXJxdWV0IikpCmBgYAoKIyBwbG90CmBgYHtyfQojIHJlYWQgaW4gZGF0YQpkdWFsX3NlbGVjdGVkX3RhdWN1bS5kZiA8LSByZWFkX3BhcnF1ZXQoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZHVhbF9zZWxlY3RlZF90YXVjdW0ucGFycXVldCIpKQoKIyBub3JtYWxpemUgZml0bmVzcwpkdWFsX3NlbGVjdGVkX3RhdWN1bS5kZl9wIDwtIGR1YWxfc2VsZWN0ZWRfdGF1Y3VtLmRmICU+JSBtdXRhdGUoZml0bmVzc19ub3JtID0gdmFsdWUvOS44ODM2MDIpCgoKIyBwbG90IGN1bXVsdGF0aXZlIHRyYW5zbWlzc2lvbiBwbG90CmdncGxvdCgpICsKICBnZW9tX2xpbmUoZGF0YSA9IGR1YWxfc2VsZWN0ZWRfdGF1Y3VtLmRmX3AsIGFlcyhjb2xvciA9IGxhYmVsX25ldywgeCA9IHRpbWUsIHkgPSBmaXRuZXNzX25vcm0pLCBzaXplID0gMSkgKwogIGdlb21fcG9pbnQoZGF0YSA9IGR1YWxfc2VsZWN0ZWRfdGF1Y3VtLmRmX3AgJT4lIGZpbHRlcih0aW1lJSUxID09IDApLCBhZXMoY29sb3IgPSBsYWJlbF9uZXcsIHggPSB0aW1lLCB5ID0gZml0bmVzc19ub3JtLCBzaGFwZSA9IGxhYmVsX25ldyksIHNpemUgPSAzKSArCiAgbGFicyh4ID0gIlRpbWUgKGRheXMpIiwgeSA9ICJDdW11bGF0aXZlIHRyYW5zbWlzc2lvblxucG90ZW50aWFsIChub3JtYWxpemVkKSIsIGNvbG9yID0gIkN1ZShzKSIsIHNoYXBlID0gIkN1ZShzKSIpICsKICB4bGltKDAsIDIwKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMob3JhbmdlLCIjRTFCRTZBIixibHVlICwiYmxhY2siKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDQsIGJ5cm93ID0gVFJVRSkpCgpnZ3NhdmUoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvZHVhbF90YXVjdW0udGlmZiIpLCB1bml0cyA9ICJweCIsIHdpZHRoID0gMTYwMCwgaGVpZ2h0ID0gMTAwMCwgZHBpPTMwMCwgIGJnID0gIndoaXRlIiwgc2NhbGUgPSAxLjIpCgpgYGAKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIEV4cGVyaW1lbnRhbCBkaXNlYXNlIG1hcHMgb2YgUC4gY2hhYmF1ZGkKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyMgSW1wb3J0IGluIGRhdGEKYGBge3J9CiMgaW1wb3J0IGluIGh0dHBzOi8vYWNhZGVtaWMub3VwLmNvbS9lbXBoL2FydGljbGUvMjAxOC8xLzEyNy81MDQ1ODcxP2xvZ2luPXRydWUKIyMgKDIwMTggcHVibGlzaGVkIGluIEVNUEgpCmVtcGhfMjAxOCA8LSByZWFkeGw6OnJlYWRfeGxzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9leHBlcmltZW50YWxfZGF0YS9IdWlqYmVuXzIwMThfRU1QSC54bHMiKSwgc2hlZXQgPSAxKQoKIyBpbXBvcnQgaW4gaHR0cHM6Ly9vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvMTAuMTExMS9qLjE1NTgtNTY0Ni4yMDEwLjAxMDY4LngKIyMgKDIwMTAgcHVibGlzaGVkIGluIEV2b2x1dGlvbikKZXZvXzIwMTAgPC0gcmVhZHhsOjpyZWFkX3hscyhoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZXhwZXJpbWVudGFsX2RhdGEvSHVpamJlbl8yMDEwX2V2b2x1dGlvbi54bHMiKSwgc2hlZXQgPSAxKQoKIyBpbXBvcnQgaW4gaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zcGF0aG9nZW5zL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBwYXQuMTAwMzU3OCM6fjp0ZXh0PVRoZSUyMHBoaWxvc29waHklMjBpcyUyMHRoYXQlMjBhZ2dyZXNzaXZlLGxvbmdlciUyMGZlZWwlMjBzaWNrJTIwJTVCMTMlNUQuCiMjICgyMDEzIGluIFBMb1MgcGF0aG9nZW4pCnBsb3NfMjAxM18xIDwtIHJlYWR4bDo6cmVhZF94bHN4KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9leHBlcmltZW50YWxfZGF0YS9IdWlqYmVuXzIwMTNfUExvUy54bHN4IiksIHNoZWV0ID0gMikKCnBsb3NfMjAxM18yIDwtIHJlYWR4bDo6cmVhZF94bHN4KGhlcmUoImNvZGVfcmVwb3NpdG9yeS9leHBlcmltZW50YWxfZGF0YS9IdWlqYmVuXzIwMTNfUExvUy54bHN4IiksIHNoZWV0ID0gMykKCiMgaW1wb3J0IGluIGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pLzEwLjExMTEvai4xNDIwLTkxMDEuMjAxMS4wMjM2OS54CiMjICgyMDExIGluIEpvdXJuYWwgb2YgRXZvbHV0aW9uYXJ5IEJpb2xvZ3kpCmVzZWJfMjAxMSA8LSByZWFkeGw6OnJlYWRfeGxzKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9leHBlcmltZW50YWxfZGF0YS9IdWlqYmVuXzIwMTFfZXNlYi54bHMiKSwgc2hlZXQgPSAxKQoKIyBpbXBvcnQgaW4gaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DMzkzOTM1MS8KIyMgKDIwMTEgaW4gSm91cm5hbCBvZiBBbWVyaWNhbiBuYXR1cmFsaXN0KS4gTm90ZSBwcml2YXRlIGRhdGFzZXQgc28gbm90IHByb3ZpZGVkIGluIHRoZSBzdXBwbGVtZW50YXJ5IQphbW5hXzIwMTEgPC0gcmVhZHhsOjpyZWFkX3hscyhoZXJlKCJleHBlcmltZW50YWxfZGF0YS9Qb2xsaXR0XzIwMTFfbmF0dXJhbGlzdC54bHMiKSwgc2hlZXQgPSAxKQpgYGAKCiMjIENsZWFuIGRhdGEgCmBgYHtyfQojIyBmb3IgRU1QSCAyMDE4IHN0dWR5LCBpbmNsdWRlIG9ubHkgaW5mZWN0aW9uIHNlcmllcyB3aXRob3V0IGRydWdzIHdlcmUgUi1pbm9jdWx1bSBpcyBhZG1pbmlzdGVyZWQgYnkgaXRzZWxmLCB3aGljaCBpbmNsdWRlcyA2LCA3LCA4LCA5LCAxMC4gQm94IDYgaGFzIGEgc3RhcnRpbmcgaW5vY3VsdW0gbnVtYmVyIG9mIDEwXjYsIHdoaWNoIGlzIG1vc3Qgc2ltaWxhciB0byBvdGhlciBzdHVkaWVzLiBGaWx0ZXJpbmcgYmV0d2VlbiBkYXkgMy0yMSBiZWNhdXNlIHRob3NlIGFyZSB0aGUgZGF5cyB3aGVyZSB3ZSBoYXZlIHNpbmdsZSBkYXkgZGF0YS4KZW1waF8yMDE4X3NzLmRmIDwtIGVtcGhfMjAxOCAlPiUgCiAgZmlsdGVyKEJveCAlaW4lIHNlcSg2LCAxMCkgJgogICAgICAgICBkcGx5cjo6YmV0d2VlbihEYXksIDMsIDIxKSkgJT4lIAogIG11dGF0ZShkb3NlID0gY2FzZV93aGVuKAogICAgQm94ID09IDYgfiAxMF42LAogICAgQm94ID09IDcgfiAxMF41LAogICAgQm94ID09IDggfiAxMF4zLAogICAgQm94ID09IDkgfCBCb3ggPT0gMTAgfiAxMF4xCiAgKSkgJT4lIAogIG11dGF0ZShzdHJhaW4gPSAiQXM2cCIsCiAgICAgICAgIHN0dWR5ID0gImVtcGgyMDE4IiwKICAgICAgICAgc3R1ZHlfc3RyYWluID0gcGFzdGUwKHN0cmFpbiwgc3R1ZHkpLAogICAgICAgICBpZCA9IHBhc3RlMChzdHVkeSwgc3RyYWluLCBCb3gsIE1vdXNlLCAxKSwKICAgICAgICAgUkJDID0gUkJDICogKDEwXjYpKSAlPiUKICBzZWxlY3QoZGF5ID0gRGF5LAogICAgICAgICBtb3VzZSA9IE1vdXNlLCAKICAgICAgICAgUkJDLCAKICAgICAgICAgYXNleCA9IFJhc2V4LAogICAgICAgICBnYW0gPSBSZ2FtLAogICAgICAgICBkb3NlLAogICAgICAgICBzdHJhaW4sCiAgICAgICAgIHN0dWR5LAogICAgICAgICBzdHVkeV9zdHJhaW4sCiAgICAgICAgIGlkKQoKIyMgZm9yIDIwMTEgZXNlYiwgb25seSBkYXkgMy0xNyBkYXRhIGFyZSBhbmFseXplZCBiZWNhdXNlIHRob3NlIGFyZSB0aGUgZGF5cyB3aGVyZSBnYW1ldG9jeXRlIGRhdGEgYXJlIGF2YWlsYWJsZQplc2ViXzIwMTFfc3MuZGYgPC0gZXNlYl8yMDExICU+JSAKICBmaWx0ZXIoQ2xvbmVzID09ICJSIiAmIGJldHdlZW4oRGF5LCAzLCAxNykgJgogICAgICAgICAgIERydWdzID09ICJOIikgJT4lIAogIG11dGF0ZShkb3NlID0gMTBeNiwKICAgICAgICAgc3RyYWluID0gIkFzOHAiLAogICAgICAgICBzdHVkeSA9ICJlc2ViMjAxMSIsCiAgICAgICAgIHN0dWR5X3N0cmFpbiA9IHBhc3RlMChzdHJhaW4sIHN0dWR5KSwKICAgICAgICAgUkJDID0gUkJDKigxMF42KSwKICAgICAgICAgaWQgPSBwYXN0ZTAoc3R1ZHksIHN0cmFpbiwgQm94LCBNb3VzZSwgMikpICU+JSAKICBzZWxlY3QoZGF5ID0gRGF5LAogICAgICAgICBtb3VzZSA9IE1vdXNlLAogICAgICAgICBSQkMsCiAgICAgICAgIGFzZXggPSBSLmFzZXgsCiAgICAgICAgIGdhbSA9IFIuZ2FtLAogICAgICAgICBkb3NlLAogICAgICAgICBzdHJhaW4sCiAgICAgICAgIHN0dWR5LAogICAgICAgICBzdHVkeV9zdHJhaW4sCiAgICAgICAgIGlkKQoKIyMgZm9yIGV2b2x1dGlvbl8yMDEwLCBzaW5nbGUgaW5mZWN0aW9uIGRhdGEgZm9yIGJvdGggcmVzaXN0YW50IGFuZCBzdXNjZXB0aWJsZSBjbG9uZXMgYXJlIGF2YWlsYWJsZSB3aXRob3V0IGRydWcgdHJlYXRtZW50CmV2b2x1dGlvbl8yMDEwX3NzLmRmIDwtIGV2b18yMDEwICU+JSAKICBmaWx0ZXIoQ2xvbmUgPT0gIlIiIHwgQ2xvbmUgPT0gIlMiKSAlPiUKICBmaWx0ZXIoYmV0d2VlbihEYXksIDMsIDIxKSAmCiAgICAgICAgICAgRHJ1Z3MgPT0gIm5vZHJ1Z3MiKSAlPiUgCiAgbXV0YXRlKGFzZXggPSBSLmFzZXggKyBTLmFzZXgsCiAgICAgICAgIGdhbSA9IFIuZ2FtICsgUy5nYW0sCiAgICAgICAgIGRvc2UgPSAxMF42LAogICAgICAgICBSQkMgPSBSQkMqKDEwXjYpLAogICAgICAgICBzdHVkeSA9ICJldm9sMjAxMSIsCiAgICAgICAgIHN0cmFpbiA9IGlmZWxzZShDbG9uZSA9PSAiUiIsICJBczEyIiwgIkFKNTEiKSwKICAgICAgICAgc3R1ZHlfc3RyYWluID0gcGFzdGUwKHN0cmFpbiwgIl8iLCBzdHVkeSksCiAgICAgICAgIGlkID0gcGFzdGUwKHN0dWR5LCBzdHJhaW4sIEJveCwgTW91c2UsIDMpKSAlPiUgCiAgc2VsZWN0KGRheSA9IERheSwKICAgICAgICAgbW91c2UgPSBNb3VzZSwKICAgICAgICAgUkJDLAogICAgICAgICBhc2V4LAogICAgICAgICBnYW0sCiAgICAgICAgIGRvc2UsCiAgICAgICAgIHN0cmFpbiwKICAgICAgICAgc3R1ZHksCiAgICAgICAgIHN0dWR5X3N0cmFpbiwKICAgICAgICAgaWQpCgojIyBmb3IgYW1uYXQgMjAxMSwgZ2V0IHNpbmdsZSBpbmZlY3Rpb24gZGF0YS4gRmlsdGVyIG91dCBhbnkgbWljZSB0aGF0IGhhdmUgbWlzc2luZyBkYXRhLiBTZXQgbmVnYXRpdmUgYXNleHVhc2wgZGF0YSBvdCAwCmFtbmFfMjAxMV9zcy5kZiA8LSAgYW1uYV8yMDExICU+JSAKICBmaWx0ZXIodHJlYXQgJWluJSBjKCJBSiIsICJBUyIsICJFUiIsICJDUiIsICJDVyIsICJESyIpKSAlPiUgCiAgbXV0YXRlKGFzZXggPSB0b3QucGFyYSAtIHRvdC5nY3l0ZSwKICAgICAgICAgZ2FtID0gdG90LmdjeXRlLAogICAgICAgICBkb3NlID0gMTBeNiwKICAgICAgICAgc3R1ZHkgPSAiYW1uYV8yMDExIiwKICAgICAgICAgUkJDID0gcmJjLygxMF42KSwKICAgICAgICAgc3R1ZHlfc3RyYWluID0gcGFzdGUwKHRyZWF0LCAiXyIsIHN0dWR5KSwKICAgICAgICAgaWQgPSBwYXN0ZTAoc3R1ZHksIHRyZWF0LCBkaXYsIG1vdXNlLCA0KSkgJT4lIAogIG11dGF0ZShhc2V4ID0gaWZlbHNlKGFzZXggPCAwLCAwLCBhc2V4KSkgIyBzb21ldGltZXMgdG90YWwgcGFyYXNpdGUgaXMgbGVzcyB0aGFuIGdhbWV0b2N5dGUgc28gbmVlZCB0byBjb3JyZWN0IGZvciB0aGlzCgojIyMgY2hlY2sgZm9yIE5BIGJ5IGdyb3VwcwphbW5hX25hLmlkIDwtIGFtbmFfMjAxMV9zcy5kZiAlPiUgCiAgZmlsdGVyX2F0KHZhcnMoYXNleCwgZ2FtLCBSQkMpLCBhbGxfdmFycyhpcy5uYSguKSkpICU+JSAKICBkaXN0aW5jdChpZCkgJT4lIAogIHNlbGVjdChpZCkKCmFtbmFfMjAxMV9zcy5kZjIgPC0gYW1uYV8yMDExX3NzLmRmICU+JSAKICBmaWx0ZXIoIShpZCAlaW4lIGFtbmFfbmEuaWQkaWQpKSAlPiUgCiAgc2VsZWN0KGRheSwKICAgICAgICAgbW91c2UsCiAgICAgICAgIFJCQywKICAgICAgICAgYXNleCwKICAgICAgICAgZ2FtLAogICAgICAgICBkb3NlLAogICAgICAgICBzdHJhaW4gPSB0cmVhdCwKICAgICAgICAgc3R1ZHksCiAgICAgICAgIHN0dWR5X3N0cmFpbiwKICAgICAgICAgaWQpCgojIyByYmluZApleHBfc3MuZGYgPC0gcmJpbmQoZW1waF8yMDE4X3NzLmRmLCBlc2ViXzIwMTFfc3MuZGYsIGV2b2x1dGlvbl8yMDEwX3NzLmRmLCBhbW5hXzIwMTFfc3MuZGYyKQoKIyMgd3JpdGUKd3JpdGUuY3N2KGV4cF9zcy5kZiwgaGVyZSgiY29kZV9yZXBvc2l0b3J5L2RhdGEvZXhwZXJpbWVudGFsX2RhdGEuY3N2IikpCmBgYAoKIyMgUHJlcGFyZSBkYXRhc2V0IGZvciBwbG90dGluZwpgYGB7cn0KbmFtZXMoZXhwX3NzLmRmKSA8LSBjKCJYIiwgIkRheSIsICJNb3VzZSIsICJSQkMiLCAiaVJCQyIsICJHYW1ldG9jeXRlIiwgIkRvc2UiLCAiU3RyYWluIiwgIlN0dWR5IiwgIlN0dWR5X3N0cmFpbiIsICJpZCIpCgojIyBwcmVwYXJlIGEgbGlzdCBvZiB2YXJpYWJsZSBjb21iaW5hdGlvbnMgd2Ugd2FudCB0byBwbG90CmV4cF92YXIuY29tYiA8LSB0aWR5cjo6ZXhwYW5kX2dyaWQoeCA9IGMoIlJCQyIsICJpUkJDIiwgIkdhbWV0b2N5dGUiKSwKICAgICAgICAgICAgICAgICAgIHkgPSBjKCJSQkMiLCAiaVJCQyIsICJHYW1ldG9jeXRlIikpICU+JSAjIyBnZXQgYWxsIHBhaXJ3aXNlIGNvbWJpbmF0aW9ucyBvZiB2YXJpYWJsZXMKICBmaWx0ZXIoeCAhPSB5KSAlPiUgIyMgcmVtb3ZlIGluY2lkZW5jZXMgd2hlcmUgdGhlIDIgdmFyaWFibGVzIGFyZSB0aGUgc2FtZQogIG11dGF0ZSh0bXAgPSBwYXN0ZTAocG1pbih4LCB5KSwgcG1heCh4LCB5KSkpICU+JSAjIyBlbGltaW5hdGUgc2FtZSB2YXJpYWJsZSBidXQgZGlmZmVyZW50IG9yZGVyCiAgc2xpY2VfaGVhZChuID0gMSwgYnkgPSB0bXApICU+JSAKICBzZWxlY3QoLXRtcCkgCmBgYAoKIyMgTGlzdC13aXNlIHBsb3R0aW5nCmBgYHtyfQojIyB4IGFuZCB5IGF4aXMgYXJlIG5vdCBsb2dnZWQhCmV4cF94eS5wbF9scyA8LSBtYXAyKGV4cF92YXIuY29tYiR4LCBleHBfdmFyLmNvbWIkeSwgfiB7CiAgeF9jb2wgPC0gLngKICB5X2NvbCA8LSAueQogIAogIGdncGxvdChleHBfc3MuZGYsIGFlc19zdHJpbmcoeCA9IHhfY29sLCB5ID0geV9jb2wpKSArCiAgICAgICAgZ2VvbV9wYXRoKGFlcyhjb2xvdXIgPSBEYXksIGdyb3VwID0gaWQpLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLCAiaW5jaGVzIikpKSArCiAgICAgICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJBIiwgbGltaXRzID0gYygzLCAyMSkpICsKICAgICAgICBsYWJzKGNvbG9yID0gIkRheXMgcG9zdC1pbmZlY3Rpb24iKSAgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBmb3JtYXQoeCwgc2NpZW50aWZpYyA9IFRSVUUpKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX3NjaWVudGlmaWMoZGlnaXRzID0gMSkpCn0KKQoKIyMgeC1heGlzIGlzIGxvZ2dlZApleHBfeGxvZ3kucGxfbHMgPC0gbWFwMihleHBfdmFyLmNvbWIkeCwgZXhwX3Zhci5jb21iJHksIH4gewogIHhfY29sIDwtIC54CiAgeV9jb2wgPC0gLnkKICAKICBnZ3Bsb3QoZXhwX3NzLmRmLCBhZXNfc3RyaW5nKHggPSBzcHJpbnRmKCJsb2cxMCglcykiLCB4X2NvbCksIHkgPSB5X2NvbCkpICsKICAgICAgICBnZW9tX3BhdGgoYWVzKGNvbG91ciA9IERheSwgZ3JvdXAgPSBpZCksIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAsICJpbmNoZXMiKSkpICsKICAgICAgICB0aGVtZV9jbGFzc2ljKCkgKyAKICAgICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gIkEiLCBsaW1pdHMgPSBjKDMsIDIxKSkgKwogICAgICAgIGxhYnMoY29sb3IgPSAiRGF5cyBwb3N0LWluZmVjdGlvbiIsIHggPSBwYXN0ZSh4X2NvbCwgImxvZyIpKSAgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBmdW5jdGlvbih4KSBmb3JtYXQoeCwgc2NpZW50aWZpYyA9IFRSVUUpKQp9CikKCiMjIHktYXhpcyBsb2dnZWQKZXhwX3h5bG9nLnBsX2xzIDwtIG1hcDIoZXhwX3Zhci5jb21iJHgsIGV4cF92YXIuY29tYiR5LCB+IHsKICB4X2NvbCA8LSAueAogIHlfY29sIDwtIC55CiAgCiAgZ2dwbG90KGV4cF9zcy5kZiwgYWVzX3N0cmluZyh4ID0geF9jb2wsIHkgPSBzcHJpbnRmKCJsb2cxMCglcykiLCB5X2NvbCkpKSArCiAgICAgICAgZ2VvbV9wYXRoKGFlcyhjb2xvdXIgPSBEYXksIGdyb3VwID0gaWQpLCBhcnJvdyA9IGFycm93KHR5cGUgPSAiY2xvc2VkIiwgYW5nbGUgPSAxMCwgbGVuZ3RoID0gdW5pdCgwLCAiaW5jaGVzIikpKSArCiAgICAgICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICAgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJBIiwgbGltaXRzID0gYygzLCAyMSkpICsKICAgICAgICBsYWJzKGNvbG9yID0gIkRheXMgcG9zdC1pbmZlY3Rpb24iLCB5ID0gcGFzdGUoeV9jb2wsICJsb2ciKSkgICArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX3NjaWVudGlmaWMoZGlnaXRzID0gMSkpCn0KKQoKIyMgYm90aCB4IGFuZCB5IGF4aXMgaXMgbG9nZ2VkCmV4cF94bG9neWxvZy5wbF9scyA8LSBtYXAyKGV4cF92YXIuY29tYiR4LCBleHBfdmFyLmNvbWIkeSwgfiB7CiAgeF9jb2wgPC0gLngKICB5X2NvbCA8LSAueQogIAogIGdncGxvdChleHBfc3MuZGYsIGFlc19zdHJpbmcoeCA9IHNwcmludGYoImxvZzEwKCVzKSIsIHhfY29sKSwgeSA9IHNwcmludGYoImxvZzEwKCVzKSIsIHlfY29sKSkpICsKICAgICAgICBnZW9tX3BhdGgoYWVzKGNvbG91ciA9IERheSwgZ3JvdXAgPSBpZCksIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAsICJpbmNoZXMiKSkpICsKICAgICAgICB0aGVtZV9jbGFzc2ljKCkgKyAKICAgICAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gIkEiLCBsaW1pdHMgPSBjKDMsIDIxKSkgKwogICAgICAgIGxhYnMoY29sb3IgPSAiRGF5cyBwb3N0LWluZmVjdGlvbiIsIHggPSBwYXN0ZSh4X2NvbCwgImxvZyIpLCB5ID0gcGFzdGUoeV9jb2wsICJsb2ciKSkgCn0KKQoKIyMgcGxvdCB0b2dldGhlcgpnZ2FycmFuZ2UocGxvdGxpc3QgPSBjKGV4cF94eS5wbF9scywgZXhwX3hsb2d5LnBsX2xzLCBleHBfeHlsb2cucGxfbHMsIGV4cF94bG9neWxvZy5wbF9scyksIAogICAgICAgICAgY29tbW9uLmxlZ2VuZCA9IFQsIGFsaWduID0gImh2IikKZ2dzYXZlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL2V4cF9kaXNlYXNlLWN1cnZlLnRpZmYiKSwgdW5pdHMgPSAicHgiLCB3aWR0aCA9IDIyNTAsIGhlaWdodCA9IDE1MDAsIHNjYWxlID0gMS40LCBkcGk9MzAwLCAgYmcgPSAid2hpdGUiKQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgU2ltdWxhdGVkIGRpc2Vhc2UgY3VydmUgZ3JhcGgKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyMgRnVuY3Rpb24gdG8gb2J0YWluIGR5bmFtaWNzIGRhdGEgYmFzZWQgb24gdGhlIGR1YWwgY3VlIGlucHV0CmBgYHtyfQpnZXRfZHVhbF9ybiA8LSBmdW5jdGlvbihkZil7CiAgIyMgYXNzaWduIHRoZSB0d28gY3VlcwogIGN1ZSA8LSB1bmlxdWUoZGYkY3VlKQogIGN1ZV9iIDwtIHVuaXF1ZShkZiRjdWVfYikKICAKICAjIyBhc3NpZ24gbG9nIHN0YXR1cwogIGxvZyA8LSBpZmVsc2Uoc3RyX2RldGVjdCh1bmlxdWUoZGYkaWQpLCAibG9nIiksICJsb2ciLCAibm9uZSIpCiAgbG9nX2IgPC0gaWZlbHNlKHN0cl9kZXRlY3QodW5pcXVlKGRmJGlkX2IpLCAibG9nIiksICJsb2ciLCAibm9uZSIpCiAgCiAgIyMgYXNzaWduIHdoaWNoIGN1ZXMgYXJlIGdvaW5nIHRvIGJlIGxvZ2dlZAogIGlmKGxvZyA9PSAibG9nIiAmIGxvZ19iID09ICJub25lIil7bG9nZ2VkX2N1ZSA8LSBjdWV9CiAgaWYobG9nID09ICJub25lIiAmIGxvZ19iID09ICJsb2ciKXtsb2dnZWRfY3VlIDwtIGN1ZV9ifQogIGlmKGxvZyA9PSAibG9nIiAmIGxvZ19iID09ICJsb2ciKXtsb2dnZWRfY3VlIDwtIGMoY3VlLCBjdWVfYil9CiAgaWYobG9nID09ICJub25lIiAmIGxvZ19iID09ICJub25lIil7bG9nZ2VkX2N1ZSA8LSBjKCl9CgogICMjIGtlZXAgdmFyaWFibGVzIHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIGN1ZSB1c2VkCiAgIyMjIGZvciBkYXRhZnJhbWVzIHRoYXQgZG9lcyBub3QgaW52b2x2ZSBjb21iaW5lZCB2YXJpYWJsZXMgc3VjaCBhcyBJK0lnCiAgaWYoaXNUUlVFKHN0cl9kZXRlY3QoY3VlLCAiXFwrIiwgbmVnYXRlID0gVCkpICYgaXNUUlVFKHN0cl9kZXRlY3QoY3VlX2IsICJcXCsiLCBuZWdhdGUgPSBUKSkpewogICAgZGZfZiA8LSBkZiAlPiUgCiAgICAgIGZpbHRlcih2YXJpYWJsZSAlaW4lIGMoY3VlLCBjdWVfYiwgImNyIikpICU+JSAKICAgICAgbXV0YXRlKHZhbHVlID0gY2FzZV93aGVuKAogICAgICAgIHZhcmlhYmxlICVpbiUgbG9nZ2VkX2N1ZSB+IGxvZzEwKHZhbHVlKSwKICAgICAgICBUUlVFIH4gdmFsdWUKICAgICAgKSkgJT4lICMjIGxvZyB0cmFuc2Zvcm0gdmFsdWVzIG9ubHkgd2hlbiB0aGV5IG1hdGNoIHdpdGggdGhlIGxvZ2dlZCBjdWUgbGlzdAogICAgICBmaWx0ZXIodmFsdWUgPj0gMCkgIyMgZmlsdGVyIG91dCB2YWx1ZXMgPDAsIHRoZXNlIGhhcHBlbiBkdWUgdG8gc3RpZmZuZXNzIG9mIG1vZGVscyBidXQgYXJlIG5vdCByZWxldmFudAogICAgICAgICAKICB9IGVsc2V7CiAgICAjIyMgYXNzaWduIGJvdGggY3VlcyB0byBhIGxpc3QKICAgIGN1ZV9scyA8LSBjKGN1ZSwgY3VlX2IpCiAgICAjIyMgcGljayB0aGUgY3VlIHRoYXQgaGFzIHRoZSAiKyIgc2lnbgogICAgY29tYmluZWRfY3VlIDwtIGN1ZV9sc1tncmVwbCgiXFwrIiwgY3VlX2xzKV0KICAgIG5vbl9jb21iaW5lZF9jdWUgPC0gY3VlX2xzWyFncmVwbCgiXFwrIiwgY3VlX2xzKV0gIyMjIHRoaXMgaXMgdGhlIG5vbmUgY29tYmluZWQgY3VlCiAgICAjIyMgdW5saXN0IHRoZSBjdWVzCiAgICBjdWVfdW5saXN0IDwtIHVubGlzdChzdHJfc3BsaXQoY29tYmluZWRfY3VlLCAiXFwrIikpCiAgICAKICAgICMjIyBnZXQgZmlsdGVyZWQgZGF0YXNldCBjb250YWluaW5nIG9ubHkgbm9uLWNvbWJpbmUgY3VlCiAgICBkZl9mMSA8LSBkZiAlPiUgCiAgICAgIGZpbHRlcih2YXJpYWJsZSAlaW4lIGMobm9uX2NvbWJpbmVkX2N1ZSwgImNyIikpCiAgICAKICAgICMjIyBnZXQgZmlsdGVyZWQgZGF0YXNldCBjb250YWluaW5nIGNvbWJpbmVkIGN1ZS4gVGhlc2Ugd2lsbCBiZSBzdW1tZWQgdXAgYW5kIGJvdW5kIGJhY2sgdG8gdGhlIHByZXZpb3VzCiAgICBkZl9mMiA8LSBkZiAlPiUgCiAgICAgIGZpbHRlcih2YXJpYWJsZSAlaW4lIGN1ZV91bmxpc3QpICU+JSAjIyBrZWVwIG9ubHkgdmFyaWFibGVzIHRoYXQgd2Ugd2lsbCBjb21iaW5lCiAgICAgIGdyb3VwX2J5KHRpbWUpICU+JSAjIyBmb3IgZWFjaCB0aW1lIHBvaW50LCBncm91cCB0aGUgdmFyaWFibGVzCiAgICAgIG11dGF0ZSh2YWx1ZSA9IHN1bSh2YWx1ZSwgbmEucm0gPSBUKSwKICAgICAgICAgICAgIHZhcmlhYmxlID0gY29tYmluZWRfY3VlKSAlPiUgIyMgcmVjYWxjdWxhdGUgdGhlIHZhbHVlIGFzIHN1bSBvZiB0aGUgdmFsdWVzIGFuZCByZWFzc2lnbiB2YXJpYWJsZSEKICAgICAgZGlzdGluY3QodGltZSwgLmtlZXBfYWxsID0gVCkgIyMgbm90ZSB0aGF0IGJlY2F1c2Ugd2UgYXJlIG11dGF0aW5nIHdlIG11c3QgZGVkZXVwbGljYXRlIHRoZSByZWNvcmRzCiAgICAKICAgICMjIyMgY29tYmluZSB0aGUgdHdvIGFuZCBsb2cgdHJhbnNmb3JtIGlmIG5lY2Vzc2FyeQogICAgZGZfZiA8LSByYmluZChkZl9mMSwgZGZfZjIpICU+JSAKICAgICAgbXV0YXRlKHZhbHVlID0gaWZlbHNlKHZhcmlhYmxlICVpbiUgbG9nZ2VkX2N1ZSwgbG9nMTAodmFsdWUpLCB2YWx1ZSkpICU+JSAjIyBsb2cgdHJhbnNmb3JtIHZhbHVlcyBvbmx5IHdoZW4gdGhleSBtYXRjaCB3aXRoIHRoZSBsb2dnZWQgY3VlIGxpc3QKICAgICAgZmlsdGVyKHZhbHVlID49IDApIAogIH0KICAKICAjIyBDb252ZXJ0IGRhdGFmcmFtZXMgd2lkZXIgc3VjaCB0aGF0IHRoZSBkaWZmZXJlbnQgdmFyaWFibGVzIGhhdmUgdGhlaXIgb3duIGNvbHVtbnMKICBkZl9mcCA8LSBkZl9mICU+JSAKICAgIG11dGF0ZSh2YXJpYWJsZV9pZCA9IGlmZWxzZSh2YXJpYWJsZSA9PSBjdWUsIHBhc3RlMCh2YXJpYWJsZSwgIl8iLCBsb2cpLCBwYXN0ZTAodmFyaWFibGUsICJfIiwgbG9nX2IpKSkgJT4lICAjIyMgYXNzaWduIGEgdW5pcXVlIHZhcmlhYmxlIGlkIHRoYXQgY291bGQgbGF0ZXIgYmUgdXNlZCB0byBhc3NpZ24gbGFiZWxzCiAgICBsZWZ0X2pvaW4oc2VsZWN0KGV6X2xhYmVsLCBpZCwgbG9uZ19sYWJlbCksIGJ5ID0gYygidmFyaWFibGVfaWQiID0gImlkIikpICU+JSAKICAgIG11dGF0ZShsb25nX2xhYmVsID0gaWZlbHNlKHZhcmlhYmxlID09ICJjciIsICJjciIsIGxvbmdfbGFiZWwpKSAlPiUgIyMgbWFudWFsbHkgYWRkIGNyCiAgICBtdXRhdGUobG9uZ19sYWJlbCA9IGdzdWIoIiAiLCAiXyIsIGxvbmdfbGFiZWwpKSAlPiUgICMjIGNvbnZlcnQgc3BhY2VzIHRvIF8gZm9yIHBsb3R0aW5nCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbG9uZ19sYWJlbCwgdmFsdWVzX2Zyb20gPSB2YWx1ZSwgaWRfY29scyA9IGModGltZSwgaWQsIGlkX2IpKSAlPiUgCiAgICBmaWx0ZXIodGltZSA+PSAxKSAlPiUgIyMgZmlsdGVyIG91dCBkYXkgMC0+MSBiZWNhdXNlIGFsbCBjciA9IDAgYmVmb3JlIHRoYXQKICAgIGFycmFuZ2UodGltZSkgIyMgdGhpcyBpcyBuZWVkZWQgdG8gcHJldmVudCBnZW9tX3BhdGggZnJvbSBqb2luaW5nIHRoZSBmaXJzdCBhbmQgbGFzdCBkYXRhIHBvaW50CiAgCiAgIyMgYXNzaWduIE5BcyAobWVhbmluZyBubyBzdHVmZiBpcyBwcm9kdWNlZCB5ZXQgdG8gMCkKICBkZl9mcFtpcy5uYShkZl9mcCldIDwtIDAKCiAgcmV0dXJuKGRmX2ZwKQp9CmBgYAoKIyMgUnVuIGZ1bmN0aW9uIHRvIGdldCBhIGN1cmF0ZWQgZGF0YXNldCBjb250YWluaW5nIG9ubHkgcmVsZXZhbnQgCmBgYHtyfQojIyBzcGxpdCBkdWFsIGR5bmFtaWNzIGRhdGFmcmFtZSBpbnRvIGxpc3QgZ3JvdXBlZCBieSB0aGUgZHVhbCBjdWVzCmR1YWxfY3VlX2R5bi5scyA8LSBkdWFsX2N1ZV9keW4uZGYgJT4lIGdyb3VwX3NwbGl0KGlkLCBpZF9iKQoKIyMgcnVuIGZ1bmN0aW9uIGFjcm9zcyBsaXN0CmR1YWxfY3VlX3JuLmxzIDwtIG1jbGFwcGx5KGR1YWxfY3VlX2R5bi5scywgZ2V0X2R1YWxfcm4sIG1jLmNvcmVzID0gNikKCiMjIHNhbml0eSBjaGVja3MgdGhhdCB3ZSBhcmUgYWN0dWFsbHkgc3VtbWluZyBpUkJDcy4gUmluZ3Mgb3V0Cm1heChkdWFsX2N1ZV9ybi5sc1tbMl1dJFRvdGFsX2lSQkMpCm1heChkdWFsX2N1ZV9ybi5sc1tbNF1dJEFzZXh1YWxfaVJCQykKbWF4KGR1YWxfY3VlX3JuLmxzW1s2XV0kU2V4dWFsX2lSQkMpCgptYXgoZHVhbF9jdWVfcm4ubHNbWzEwXV0kVG90YWxfaVJCQykKbWF4KGR1YWxfY3VlX3JuLmxzW1sxMl1dJEFzZXh1YWxfaVJCQykKbWF4KGR1YWxfY3VlX3JuLmxzW1sxNF1dJFNleHVhbF9pUkJDKQpgYGAKCiMjIHBsb3QKYGBge3J9CiMjIGxpc3QgYXBwbHkgYWxsIGRhdGFmcmFtZXMKY3VlX2N1ZV9ybl9wbC5scyA8LSBsYXBwbHkoZHVhbF9jdWVfcm4ubHMsCiAgICAgICBmdW5jdGlvbih4KXsKICAgICAgICAgICMjIGdldCBuYW1lcyBvZiBjb2x1bW5zIHVzZWQgaW4gdGhlIHggYW5kIHkgYXhpcwogICAgICAgICBheGlzX2NvbHMgPC0gc2V0ZGlmZihuYW1lcyh4KSwgYygidGltZSIsICJpZCIsICJpZF9iIiwgImNyIikpCiAgICAgICAgIAogICAgICAgICAjIyBnZ3Bsb3QKICAgICAgICAgZ2dwbG90KCkgKwogICAgICAgICAgIGdlb21fcGF0aChkYXRhID0geCwgYWVzX3N0cmluZyh4ID0gYXhpc19jb2xzW1sxXV0sIHkgPSBheGlzX2NvbHNbWzJdXSwgY29sb3IgPSAiY3IiKSwKICAgICAgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KGMocmVwKDAsIG5yb3coeCkgLSAyKSwgMC4yNSksICJpbmNoZXMiKSksCiAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAxLjUpICsKICAgICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSB4ICU+JSBmaWx0ZXIodGltZSAlJSAxID09IDApLCAKICAgICAgICAgICAgICAgICAgICAgIGFlc19zdHJpbmcoeCA9IGF4aXNfY29sc1tbMV1dLCB5ID0gYXhpc19jb2xzW1syXV0pLCBzaXplID0gMS41LCBzaGFwZSA9IDEpICsKICAgICAgICAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgICAgICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhsaW1pdHMgPSBjKDAsIDEpKSArCiAgICAgICAgICAgbGFicyhjb2xvciA9ICJDb252ZXJzaW9uIHJhdGUiLCB4ID0gZ3N1YigiXyIsICIgIiwgYXhpc19jb2xzW1sxXV0pLCB5ID0gZ3N1YigiXyIsICIgIiwgYXhpc19jb2xzW1syXV0pKSArCiAgICAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX3NjaWVudGlmaWMoZGlnaXRzID0gMikpICsKICAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfc2NpZW50aWZpYyhkaWdpdHMgPSAyKSkKICAgICAgIH0pCgojIyBhcnJhbmdlIHRvZ2V0aGVyCmN1ZV9jdWVfcm4ucGwgPC0gZ2dhcnJhbmdlKHBsb3RsaXN0ID0gY3VlX2N1ZV9ybl9wbC5scywgbmNvbCA9IDUsIG5yb3cgPSA4LCBjb21tb24ubGVnZW5kID0gVCwgYWxpZ24gPSAiaHYiKSAKZ2dzYXZlKGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL3NpbV9kaXNlYXNlLWN1cnZlLnRpZmYiKSwgdW5pdHMgPSAicHgiLCB3aWR0aCA9IDIyNTAsIGhlaWdodCA9IDI1MDAsIHNjYWxlID0gMi4yLCBkcGk9MzAwLCAgYmcgPSAid2hpdGUiKQpgYGAKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgQ3VyYXRpbmcgbGlzdCBvZiBzZWxlY3RlZCBleG8gYW5kIHNpbXVsYXRlZAojIGRpc2Vhc2UgY3VydmVzIChtYWluIGZpZ3VyZSkKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBwbG90CmBgYHtyfQojIG1haW4gZmlndXJlCmdnYXJyYW5nZSgKICAgY3VlX2N1ZV9ybl9wbC5sc1tbMzFdXSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMC4xKSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLCAKICAgICAgICAgIAogICBleHBfeGxvZ3lsb2cucGxfbHNbWzFdXSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCgogICBjdWVfY3VlX3JuX3BsLmxzW1szNV1dICsgCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArCiAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTYuNSkpLCAKICAgCiAgIGV4cF94eWxvZy5wbF9sc1tbMV1dICsgCiAgICAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6bnVtYmVyX2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArCiAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChzaXplPTYuNSkpLAogICAKICAgY3VlX2N1ZV9ybl9wbC5sc1tbMzBdXSArIAogICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEpKSArCiAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgIAogICBleHBfeGxvZ3kucGxfbHNbWzFdXSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAKICAgY3VlX2N1ZV9ybl9wbC5sc1tbMTVdXSArIAogICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEpKSArCiAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgCiAgIAogICBleHBfeGxvZ3kucGxfbHNbWzJdXSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgIAogICBhbGlnbiA9ICJodiIsIG5jb2wgPSA0LCBucm93ID0gMgopCmdnc2F2ZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9zaW1fZXhwX2Rpc2Vhc2VfY3VydmVfbWFpbi50aWZmIiksIHVuaXRzID0gInB4Iiwgd2lkdGggPSAyMjUwLCBoZWlnaHQgPSAxMDAwLCBzY2FsZSA9IDEuMjUsIGRwaT0zMDAsICBiZyA9ICJ3aGl0ZSIpCgoKIyBnZXQgbGVnZW5kIHNlcGFyYXRlbHkKZ2dhcnJhbmdlKAogICBjdWVfY3VlX3JuX3BsLmxzW1szMV1dICsKICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcl9mb3JtYXQoYWNjdXJhY3kgPSAwLjEpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpudW1iZXJfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSwgCiAgICAgICAgICAKICAgZXhwX3hsb2d5bG9nLnBsX2xzW1sxXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikpCmdnc2F2ZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9zaW1fZXhwX2Rpc2Vhc2VfY3VydmVfbGVnZW5kLnRpZmYiKSwgdW5pdHMgPSAicHgiLCB3aWR0aCA9IDIyNTAsIGhlaWdodCA9IDUwMCwgc2NhbGUgPSAxLjI1LCBkcGk9MzAwLCAgYmcgPSAid2hpdGUiKQoKYGBgCgoKCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgTUMgc2ltdWxhdGlvbiBvZiBzaW5nbGUgY3VlIGFuZCBkdWFsIGN1ZSBpbmZlY3Rpb24KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIy0tLS0tLS0tLS0tLSBJbXBhY3Qgb2YgYWxsIHBhcmFtZXRlciB2YXJpYXRpb24gb24gZml0bmVzcyAtLS0tLS0tLS0tLS0jCiMjIEFwcGVuZCBhbGwgZml0bmVzcyBkYXRhIGFuZCBzYW5pdHkgY2hlY2tzCmBgYHtyfQojIyBJbXBvcnQgYWxsIGZpdG5lc3MgZGF0YSBhbmQgbWFrZSBpbnRvIHNpbmdsZSBkYXRhZnJhbWUKbWNfYWxsLmxzIDwtIGxpc3QuZmlsZXMocGF0aCA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX2FsbF9maXRuZXNzIiksIHBhdHRlcm4gPSAiKi5jc3YiLCBmdWxsLm5hbWVzID0gVCkKCiMjIGZpbHRlciBvdXQgTUMgcmVjb3JkcyBkdWFsIGN1ZXMuIFRoZXNlIGRhdGEgaGF2ZSBkaWZmZXJlbnQgaGVhZGVycyBhbmQgd291bGQgbmVlZCB0byBiZSBwcm9jZXNzZWQgZGlmZmVyZW50bHkKbWNfYWxsX3NjLmxzIDwtIG1jX2FsbC5sc1shZ3JlcGwoIi0iLCBiYXNlbmFtZShtY19hbGwubHMpKV0KbWNfYWxsX2RjLmxzIDwtIG1jX2FsbC5sc1tncmVwbCgiLSIsIGJhc2VuYW1lKG1jX2FsbC5scykpXQoKIyMgd2UgYXJlIGV4cGVjdGluZyAxMSo1MDEwID0gNTUxMTAgZGF0YSBmaWxlcyBmb3Igc2luZ2xlIGN1ZSBhbmQgNCo1MDEwPTIwMDQwIGZpbGVzIGZvciBkYwpsZW5ndGgobWNfYWxsX3NjLmxzKQpsZW5ndGgobWNfYWxsX2RjLmxzKQoKIyMgcmVhZCBhbmQgYXBwZW5kCm1jX2FsbF9maXRuZXNzX3NjLmRmIDwtIGRvLmNhbGwocmJpbmQsIG1jbGFwcGx5KG1jX2FsbF9zYy5scywgZnVuY3Rpb24oeCl7ZGYgPC0gcmVhZC5jc3YoeCl9LCBtYy5jb3JlcyA9IDYpKQptY19hbGxfZml0bmVzc19kYy5kZiA8LSBkby5jYWxsKHJiaW5kLCBtY2xhcHBseShtY19hbGxfZGMubHMsIGZ1bmN0aW9uKHgpe2RmIDwtIHJlYWQuY3N2KHgpfSwgbWMuY29yZXMgPSA2KSkKCiMjIGNoZWNrICMgb2YgcmVwcyBwZXIgY3VlLiBsb29rcyBnb29kIQptY19hbGxfZml0bmVzc19zYy5kZiAlPiUgCiAgZGlzdGluY3QoY3VlLCBsb2csIGlkKSAlPiUgCiAgZ3JvdXBfYnkoY3VlLCBsb2cpICU+JSAKICB0YWxseSgpCgptY19hbGxfZml0bmVzc19kYy5kZiAlPiUgCiAgZGlzdGluY3QoY3VlLCBjdWVfYiwgbG9nLCBsb2dfYiwgaWQpICU+JSAKICBncm91cF9ieShjdWUsIGN1ZV9iLCBsb2csIGxvZ19iKSAlPiUgCiAgdGFsbHkoKQoKIyMgY29tYmluZSBkYXRhZnJhbWVzCm1jX2FsbF9maXRuZXNzLmRmIDwtIG1jX2FsbF9maXRuZXNzX2RjLmRmICU+JSAKICBtdXRhdGUoaXRlciA9IGlkLAogICAgICAgICBpZCA9IGdzdWIoImxvZzEwIiwgImxvZyIsIHBhc3RlMChjdWUsICJfIiwgbG9nLCAiLSIsIGN1ZV9iLCJfIiwgbG9nX2IpKSkgJT4lCiAgc2VsZWN0KGlkLCBpdGVyLCBtYXhfZml0bmVzcykgJT4lIAogIHJiaW5kKAogICAgbWNfYWxsX2ZpdG5lc3Nfc2MuZGYgJT4lIAogICAgICBtdXRhdGUoaXRlciA9IGlkLAogICAgICAgICAgICAgaWQgPSBpZmVsc2UoY3VlICE9ICJ0IiwgcGFzdGUwKGN1ZSwgIl8iLCBnc3ViKCJsb2cxMCIsICJsb2ciLCBsb2cpKSwgInRpbWUiKQogICAgICAgICAgICAgKSAlPiUgCiAgICAgIHNlbGVjdChpZCwgaXRlciwgbWF4X2ZpdG5lc3MpCiAgICApICU+JSAKICBsZWZ0X2pvaW4oZXpfbGFiZWwsIGJ5ID0gImlkIikgCiMjIGZvciBlYWNoIGN1ZSBhbmQgbG9nLCBpZCA9IDEgaXMgd2hlcmUgYWxsIHBhcmFtZXRlcnMgYXJlIGRlZmF1bHQgdmFsdWVzLiBHaXZlbiB0aGF0IHdlIHNpbXVsYXRlZCB0aGUgdGltZSBzdGVwIHdpdGggMC4wMSByYXRoZXIgdGhhbiAgMC4wMDEsIHdlIG5lZWQgdG8gY2hlY2sgd2hldGhlciB0aGlzIGhpZ2hlciB0aW1lIHN0ZXAgYWx0ZXJzIHRoZSBmaXRuZXNzIHZhbHVlcy4gV2UgY2FuIHNlZSB0aGF0IHRoZSBkaWZmZXJlbmNlIGlzIHZlcnkgc21hbGwgfjAuMDAyIHNvIHRoaXMgd2lsbCBub3QgYWZmZWN0IGFueSBvdXIgb3V0Y29tZXMuCiMjIEhlcmUsIEkgYWxzbyBtYW51YWxseSB2ZXJpZmllZCB0aGF0IHRoZSBmaXRuZXNzIHZhbHVlcyBmb3IgdGhlIGR1YWwgY3VlIGxvb2tlZCBnb29kIQptY19hbGxfZml0bmVzcy5kZiAlPiUgZmlsdGVyKGl0ZXIgPT0gMSkgJT4lIAogIGxlZnRfam9pbihzZWxlY3Qoc2lfb3B0LmRmLCBjdWUsIGxvZywgZml0bmVzc18yMCksIGJ5ID0gYygiY3VlIiwgImxvZyIpKSAlPiUgCiAgbXV0YXRlKGRpZmYgPSBtYXhfZml0bmVzcyAtIGZpdG5lc3NfMjApCgojIyB3cml0ZQp3cml0ZV9wYXJxdWV0KG1jX2FsbF9maXRuZXNzLmRmLCBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19hbGxfZml0bmVzcy5wYXJxdWV0IikpCmBgYAoKIyMgcHJvY2VzcyBkYXRhIGZvciBwbG90dGluZyBmaXRuZXNzCmBgYHtyfQojIyBub3JtYWxpemUgZml0bmVzcyBhbmQgdGFrZSBvbmx5IGZpcnN0IDEwMDAgbWVhc3VyZW1lbnQgKHRoaXMgaXMgZG9uZSBnaXZlbiB0aGF0IHN1YnNlcXVlbnQgc2ltdWxhdGlvbnMgb25seSBpbmNsdWRlIDEwMDAgcmVwcyEpCm1jX2FsbF9maXRuZXNzLmRmX3AgPC0gbWNfYWxsX2ZpdG5lc3MuZGYgJT4lIAogIGZpbHRlcihpdGVyICVpbiUgc2VxKDEsMTAwMCwxKSkgJT4lIAogIG11dGF0ZShmaXRuZXNzX25vcm0gPSBtYXhfZml0bmVzcy85Ljg4MzYwMiwKICAgICAgICAgc2hvcnRfbGFiZWwgPSBnc3ViKCJJXFwrSWciLCAiVG90YWwgSSIsIHNob3J0X2xhYmVsKSkKCiMjIGdldCB0aGUgcmVmZXJlbmNlIGZpdG5lc3MgKGRlZmF1bHQgcGFyYW1ldGVyIHZhcmlhdGlvbiksIHdoaWNoIGlzIHdoZXJlIGl0ZXIgPSAxCm1jX2FsbF9maXRuZXNzX3JlZi5kZiA8LSBtY19hbGxfZml0bmVzcy5kZl9wICU+JSBmaWx0ZXIoaXRlciA9PSAxKQoKIyMgZ2V0IHRoZSByZXN0IG9mIHRoZSBkYXRhIHBvaW50cyAoZXhjbHVkaW5nIGl0ZXIgPT0gMSkgYW5kIGNhbGN1bGF0ZSBtZWRpYW4gYW5kIG1lYW4KbWNfYWxsX2ZpdG5lc3NfcmFuZC5kZiA8LSBtY19hbGxfZml0bmVzcy5kZl9wICU+JSAKICBmaWx0ZXIoaXRlciAhPSAxKSAKCiMjIGdldCBtZWFuIGFuZCBtb2RlIGluIGEgc2VwYXJhdGUgZGYKbWNfYWxsX2ZpdG5lc3Nfc3VtLmRmIDwtIG1jX2FsbF9maXRuZXNzX3JhbmQuZGYgJT4lIAogIGdyb3VwX2J5KHNob3J0X2xhYmVsKSAlPiUgCiAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKGZpdG5lc3Nfbm9ybSksCiAgICAgICAgICAgIG1lZGlhbiA9IG1lZGlhbihmaXRuZXNzX25vcm0pLAogICAgICAgICAgICBnZW9tX21lYW4gPSBleHAobWVhbihsb2coZml0bmVzc19ub3JtKSkpLAogICAgICAgICAgICBzZCA9IHNkKGZpdG5lc3Nfbm9ybSkpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KG1jX2FsbF9maXRuZXNzX3JlZi5kZiwgc2hvcnRfbGFiZWwsIG1heF9maXRuZXNzKSwgYnkgPSAic2hvcnRfbGFiZWwiKQoKbWNfYWxsX2ZpdG5lc3Nfc3VtLmRmCmBgYAoKIyMgcGxvdCBmaXRuZXNzIHZhcmlhdGlvbiAKYGBge3J9Cm1jX2FsbF9maXRuZXNzLnBsIDwtIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihkYXRhID0gbWNfYWxsX2ZpdG5lc3NfcmFuZC5kZiwgYWVzKHggPSBmaXRuZXNzX25vcm0sIHkgPSBmY3RfcmVvcmRlcihzaG9ydF9sYWJlbCwgZml0bmVzc19ub3JtLCAuZnVuID0gZnVuY3Rpb24oeCl7ZXhwKG1lYW4obG9nKHgpKSl9KSksCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodCBncmV5IiwgY29sb3IgPSAidHJhbnNwYXJlbnQiLCB0cmltID0gVCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IG1jX2FsbF9maXRuZXNzX3JlZi5kZiwgYWVzKHggPSBmaXRuZXNzX25vcm0sIHkgPSBzaG9ydF9sYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSAiRGV0ZXJtaW5pc3RpYyIsIGNvbG9yID0gIkRldGVybWluaXN0aWMiKSwgc2l6ZSA9IDMsIGFscGhhID0gMC44KSArCiAgZ2VvbV9wb2ludChkYXRhID0gbWNfYWxsX2ZpdG5lc3Nfc3VtLmRmLCBhZXMoeCA9IG1lYW4sIHkgPSBzaG9ydF9sYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9ICJNZWFuIiwgY29sb3IgPSAiTWVhbiIpLCBzaXplID0gMywgYWxwaGEgPSAwLjgpICsKICAgIGdlb21fcG9pbnQoZGF0YSA9IG1jX2FsbF9maXRuZXNzX3N1bS5kZiwgYWVzKHggPSBnZW9tX21lYW4sIHkgPSBzaG9ydF9sYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9ICJHZW9tZXRyaWMgbWVhbiIsIGNvbG9yID0gIkdlb21ldHJpYyBtZWFuIiksIHNpemUgPSAzLCBhbHBoYSA9IDAuOCkgKwogIGxhYnMoeCA9Ik5vcm1hbGl6ZWQgZml0bmVzcyIsIHkgPSAiQ3VlcyIsIHNoYXBlID0gIiIsIGNvbG9yID0gIiIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmxhY2siLCAiIzc4NUVGMCIsICIjRkZCMDAwIikpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIGV4cGFuZF9saW1pdHMoeSA9MTcpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuMiwxKSwgCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJ0b3AiKSAKYGBgCgojLS0tLS0tLS0tLS0tIEltcGFjdCBvZiBpbmRpdmlkdWFsIHBhcmFtZXRlciB2YXJpYXRpb24gb24gZml0bmVzcyAtLS0tLS0tLS0tLS0jCiMjIGNvbWJpbmUgYWxsIHNpbmdsZSBwYXJhbWV0ZXIgdmFyaWF0aW9uIGZpbGVzCmBgYHtyfQojIyBsaXN0IG9mIGZpbGUgcGF0aHMgbGlua2VkIHRvIHRoZSBzaW5nbGUgcGFyYW1ldGVyIGZpbGVzCm1jX3NpbmdsZS5scyA8LSBsaXN0LmZpbGVzKHBhdGggPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZml0bmVzcyIpLCBwYXR0ZXJuID0gIiouY3N2IiwgZnVsbC5uYW1lcyA9IFQpCgojIyByZWFkIGFuZCBjb21iaW5lCm1jX3NpbmdsZV9maXRuZXNzLmRmIDwtIGRvLmNhbGwocmJpbmQsIG1jbGFwcGx5KG1jX3NpbmdsZS5scywgZnVuY3Rpb24oeCkgcmVhZC5jc3YoeCksIG1jLmNvcmVzID0gNikpCgojIGxvb2sgYXQgIyBvZiByZXBzCm1jX3NpbmdsZV9maXRuZXNzLmRmICU+JSAKICBncm91cF9ieShpZCkgJT4lIAogIHRhbGx5KCkKCiMjIHdyaXRlCndyaXRlX3BhcnF1ZXQobWNfc2luZ2xlX2ZpdG5lc3MuZGYsIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX3NpbmdsZV9maXRuZXNzLnBhcnF1ZXQiKSkKCiMjIHNhbml0eSBjaGVjay4gaXRlciA9IDEgaXMgd2hlcmUgYWxsIHBhcmFtZXRlcnMgYXJlIGRlZmF1bHQuIFNob3VsZCBwcm9kdWNlIHRoZSBzYW1lIGZpdG5lc3MgdmFsdWVzIQptY19zaW5nbGVfZml0bmVzcy5kZiAlPiUgZmlsdGVyKGl0ZXIgPT0gMSkKYGBgCgojIyBwcm9jZXNzIGRhdGEgZm9yIHBsb3R0aW5nCmBgYHtyfQojIyBub3RlIHRoYXQgZm9yIGVhY2ggaXRlciBhY3Jvc3MgdGhlICJzaW5nbGUiIGFuZCAiYWxsIiBkYXRhc2V0LCB0aGUgcGFyYW1ldGVyIGFsdGVyYXRpb24gaXMgdGhlIHNhbWUuIFRodXMsIGZvciBlYWNoIGRhdGEgcG9pbnQgYXQgd2hpY2ggYWxsIHBhcmFtZXRlciBhcmUgdmFyaWVkLCB0aGVyZSBpcyBhIGNvcnJlc3BvbmRpbmcgZGF0YXBvaW50IHdoZXJlIG9ubHkgb25lIHBhcmFtZXRlciBpcyB2YXJpZWQuIFdlIGNhbiBqb2luIHRoZXNlIDIgZGF0YXNldCBieSBpdGVyIGFuZCBjdWUgYW5kIGxvZwptY19zaW5nbGVfYWxsX2ZpdG5lc3MuZGYgPC0gbWNfc2luZ2xlX2ZpdG5lc3MuZGYgJT4lIAogIGxlZnRfam9pbihzZWxlY3QobWNfYWxsX2ZpdG5lc3MuZGYsIGZpdG5lc3NfYWxsID0gbWF4X2ZpdG5lc3MsaWQsIGl0ZXIpLCBieSA9IGMoImlkIiwgIml0ZXIiKSkgJT4lIAogIGZpbHRlcihpdGVyICVpbiUgc2VxKDAsMTAwMCwxKSkgIyMgb25seSBpbmNsdWRlIHRoZSB0b3AgMTAwMCByZWNvcmRzCgojIyBtYWtlIHRoZSBkYXRhZnJhbWUgaW50byBhIGxvbmcgZm9ybWF0IHN1Y2ggdGhhdCBhbGwgZml0bmVzcyB2YXJpYXRpb25zIGFyZSBpbiBhIHNpbmdsZSBjb2x1bW4KbWNfc2luZ2xlX2ZpdG5lc3MubG9uZyA8LSBtY19zaW5nbGVfYWxsX2ZpdG5lc3MuZGYgJT4lIAogIGZpbHRlcihpdGVyICE9IDEpICU+JSAjIyBmaWx0ZXIgb3V0IGl0ZXIgPSAxLCB3aGljaCBkb2VzIG5vdCBoYXZlIHZhcmlhdGlvbgogIGxlZnRfam9pbihzZWxlY3QobWNfYWxsX2ZpdG5lc3NfcmVmLmRmLCBmaXRuZXNzX3JlZiA9IG1heF9maXRuZXNzLCBpZCksCiAgICAgICAgICAgIGJ5ID0gYygiaWQiKSkgJT4lICMjIGFkZCBpbiB0aGUgZGV0ZXJtaW5pc3RpYyBmaXRuZXNzIHZhbHVlcwogIHNlbGVjdChpZCwgbWF4X2ZpdG5lc3NfcmhvLCBtYXhfZml0bmVzc19iZXRhLCBtYXhfZml0bmVzc19wc2luLCBtYXhfZml0bmVzc19wc2l3LCBtYXhfZml0bmVzc19waGluLCBtYXhfZml0bmVzc19waGl3LCBpdGVyLCBmaXRuZXNzX2FsbCwgZml0bmVzc19yZWYpICU+JSAjIGtlZXAgb25seSBmaXRuZXNzIGFuZCBhc3NvY2lhdGVkIGxhYmVscwogIHRpZHlyOjpwaXZvdF9sb25nZXIoLWMoImlkIiwgImZpdG5lc3NfYWxsIiwgIml0ZXIiLCAiZml0bmVzc19yZWYiKSkgJT4lICMjIG1ha2UgbG9uZwogIG11dGF0ZShwYXJhbWV0ZXIgPSBnc3ViKCJtYXhfZml0bmVzc198Zml0bmVzc18iLCAiIiwgbmFtZSkpICMjIGlzb2xhdGUgcGFyYW1ldGVyIGJlaW5nIGFsdGVyZWQKCiMjIGNhbGN1bGF0ZSBkZWdyZWUgZGV2aWF0aW9uIGZyb20gZGV0ZXJtaW5pc3RpYyB2YWx1ZXMuIG5vdGUgdGhhdCByZWxfZGlmZl9zaW5nbGUgcmFuZ2VzIGZyb20gLTEgdG8gMS4gLTEgLT4gIG9uZSB2YXJpYWJsZSBwZXJ0dXJiYXRpb24gaXMgYWN0aW5nIGluIHRoZSBvcHBvc2l0ZSBkaXJlY3Rpb24gdG8gdGhlIG92ZXJhbGwgcGVydHVyYmF0aW9uIGNhdXNlZCBieSByYW5kb21pemluZyBhbGwgdmFyaWFibGVzLiAwIHBhcmFtZXRlciB2YXJpYXRpb24gY29udHJpYnV0ZXMgdmVyeSBsaXR0bGUsIDEgLT4gb25lIHZhcmlhYmxlIGNvbnRyaWJ1dGVzIGEgbG90Cm1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcCA8LSBtY19zaW5nbGVfZml0bmVzcy5sb25nICU+JSAKICBtdXRhdGUoZGlmZl9zaW5nbGUgPSB2YWx1ZS1maXRuZXNzX3JlZiwgIyMgcGVydHViYXRpb24gdG8gZml0bmVzcyBjYXVzZWQgYnkgc2luZ2xlIHBhcmFtZXRlciB2YXJpYXRpb24KICAgICAgICAgZGlmZl9hbGwgPSBmaXRuZXNzX2FsbC1maXRuZXNzX3JlZiwgIyMgcGVydHViYXRpb24gdG8gZmludGVzcyBjYXVzZWQgYnkgYWxsIHBhcmFtZXRlciB2YXJ5aW5nCiAgICAgICAgIHJlbF9kaWZmX3NpbmdsZSA9IGRpZmZfc2luZ2xlL2RpZmZfYWxsICMjIG5vcm1hbGl6ZWQgcGVydHVuYXRpb24gdG8gZml0bmVzcyAoc2luZ2xlIHBhcmFtZXRlcikKICAgICAgICAgKSAlPiUgCiAgbGVmdF9qb2luKGV6X2xhYmVsLCBieSA9IGMoImlkIikpCgojIyBjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzLiB0aGlzIGluY2x1ZGVzIG1lZGlhbiwgY3JlZGlibGUgaW50ZXJ2YWwgKGNvbnRhaW5zIDg5JSBkYXRhIHBvaW50cyBjYWxjdWxhdGVkIHZpYSBIaWdoZXN0IERlbnNpdHkgSW50ZXJ2YWwsIHdoaWNoIGlzIGJldHRlciBmb3Igc2tld2VkIGRhdGEpCm1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcCAlPiUgZmlsdGVyKGlkID09ICJSX2xvZy1JK0lnX2xvZyIpCgptY19zaW5nbGVfZml0bmVzcy5zdW0gPC0gbWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wICU+JSAKICBncm91cF9ieShpZCwgcGFyYW1ldGVyLCBzaG9ydF9sYWJlbCkgJT4lIAogIHN1bW1hcmlzZShjaV9sb3dlciA9IGNpKHJlbF9kaWZmX3NpbmdsZSwgbWV0aG9kID0gIkhESSIsIGNpID0gMC44OSlbWzJdXSwKICAgICAgICAgICAgY2lfaGlnaGVyID0gY2kocmVsX2RpZmZfc2luZ2xlLCBtZXRob2QgPSAiSERJIiwgY2kgPSAwLjg5KVtbM11dLAogICAgICAgICAgICBxdWFudGlsZV9sb3cgPSBxdWFudGlsZShyZWxfZGlmZl9zaW5nbGUsIDAuMDI1KSwKICAgICAgICAgICAgcXVhbnRpbGVfaGlnaCA9IHF1YW50aWxlKHJlbF9kaWZmX3NpbmdsZSwgMC45NzUpLAogICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4ocmVsX2RpZmZfc2luZ2xlKSwKICAgICAgICAgICAgbWVhbiA9IG1lYW4ocmVsX2RpZmZfc2luZ2xlKSkgJT4lIAogIG11dGF0ZShwYXJhbWV0ZXJfbGFiZWwgPSBjYXNlX3doZW4oICMjIHJlY29kZSBwYXJhbWV0ZXIgdmFsdWVzCiAgICBwYXJhbWV0ZXIgPT0gInJobyIgfiAiUkJDIHJlcGxlbmlzaG1lbnQgKM+BKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBoaW4iIH4gIkhhbGYtbGlmZSBpbmRpcyAoz5VuKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBoaXciIH4gIkhhbGYtbGlmZSB0YXJnZXRlZCAoz5V3KSIsCiAgICBwYXJhbWV0ZXIgPT0gInBzaW4iIH4gIkFjdGl2YXRpb24gaW5kaXMgKM+IbikiLAogICAgcGFyYW1ldGVyID09ICJwc2l3IiB+ICJBY3RpdmF0aW9uIHRhcmdldGVkICjPiHcpIiwKICAgIHBhcmFtZXRlciA9PSAiYmV0YSIgfiAiQnVyc3Qgc2l6ZSAozrIpIiwKICApKQoKCiMjIGZvciB2aW9saW4gcGxvdHMsIHdoYXQgd2UgY2FuIGRvIGlzIHBsb3Qgb3V0IG9ubHkgdGhlIDg5JSBjcmVkaWJsZSBpbnRlcnZhbCBzbyB0aGUgZ3JhcGggaXMgZWFzaWVyIHRvIGludGVycHJldAptY19zaW5nbGVfZml0bmVzcy5sb25nX3BfZiA8LSBtY19zaW5nbGVfZml0bmVzcy5sb25nX3AgJT4lIAogIGxlZnRfam9pbihzZWxlY3QobWNfc2luZ2xlX2ZpdG5lc3Muc3VtLCBzaG9ydF9sYWJlbCwgcGFyYW1ldGVyLCBjaV9sb3dlciwgY2lfaGlnaGVyLCBwYXJhbWV0ZXJfbGFiZWwpLCBieSA9IGMoInNob3J0X2xhYmVsIiwgInBhcmFtZXRlciIpKSAlPiUgCiAgZmlsdGVyKHJlbF9kaWZmX3NpbmdsZSA+PSBjaV9sb3dlciAmIHJlbF9kaWZmX3NpbmdsZSA8IGNpX2hpZ2hlcikKCiMjIGFycmFuZ2Ugb3JkZXJpbmcgb2YgY3VlcyBhbmQgcGFyYW1ldGVycwptY19zaW5nbGVfZml0bmVzcy5sb25nX3BfZiRzaG9ydF9sYWJlbCA8LSBmYWN0b3IobWNfc2luZ2xlX2ZpdG5lc3MubG9uZ19wX2Ykc2hvcnRfbGFiZWwsIGMoCiAgIlIgJiBJIGxvZyIsICJSIGxvZyAmIEkgbG9nIiwKICAiUiAmIFRvdGFsIEkgbG9nIiwiUiBsb2cgJiBUb3RhbCBJIGxvZyIsCiAgIlIiLCAiUiBsb2ciLAogICJJIiwgIkkgbG9nIiwKICAiSWciLCAiSWcgbG9nIiwgCiAgIlRvdGFsIEkiLCAiVG90YWwgSSBsb2ciLAogICJHIiwgIkcgbG9nIikpCgptY19zaW5nbGVfZml0bmVzcy5sb25nX3BfZiRwYXJhbWV0ZXJfbGFiZWwgPC0gZmFjdG9yKG1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcF9mJHBhcmFtZXRlcl9sYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJCdXJzdCBzaXplICjOsikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJCQyByZXBsZW5pc2htZW50ICjPgSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhhbGYtbGlmZSBpbmRpcyAoz5VuKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGFsZi1saWZlIHRhcmdldGVkICjPlXcpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY3RpdmF0aW9uIGluZGlzICjPiG4pIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY3RpdmF0aW9uIHRhcmdldGVkICjPiHcpIikpCgoKYGBgCgojIHBsb3R0aW5nIDg5JSBjcmVkaWJsZSBpbnRlcnZhbC4gQnkgdmlzdWFsIGluc3BlY3Rpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiwgY2kgcmVwcmVzZW50ZWQgdGhlIGRpc3RyaWJ1dGlvbiAobW9yZSBob25lc3RseSkgdGhhbiBxdWFudGlsZSwgZXZlbiB3aGVuIHF1YW50aWxlIHNlZW1zIHRvIGRpc3BsYXkgbGFyZ2VyIGRpZmZlcmVuY2VzIG9mIGxvZ2dpbmcgY3Vlcy4KYGBge3J9Cm1jX3BhcnRpdGlvbi5wbCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV92aW9saW4oZGF0YSA9IG1jX3NpbmdsZV9maXRuZXNzLmxvbmdfcF9mLCBhZXMoeCA9IHJlbF9kaWZmX3NpbmdsZSwgeSA9IHNob3J0X2xhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwgZmlsbCA9ICJncmV5IiwgY29sb3IgPSAiZ3JleSIpICsgIyMgODklIGNyZWRpYmxlIGludGVydmFsCiAgZ2VvbV9wb2ludChkYXRhID0gbWNfc2luZ2xlX2ZpdG5lc3Muc3VtLCBhZXMoeCA9IG1lZGlhbiwgeSA9IHNob3J0X2xhYmVsKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gIiM3ODVFRjAiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiIzc4NUVGMCIpICsKICBmYWNldF93cmFwKH5wYXJhbWV0ZXJfbGFiZWwpICsKICBsYWJzKHggPSAiUmVsYXRpdmUgZml0bmVzcyBwZXJ0dWJhdGlvbiIsIHkgPSAiQ3VlcyIpICsKICB0aGVtZV9idygpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cz1yZXYpICsgIyMgcmV2ZXJzZSBvcmRlcmluZyBvZiB5LWF4aXMgc28gdGhhdCBjdWVzIGFyZSBkaXNwbGF5ZWQgY29ycmVjdGx5CiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQogICkKYGBgCgojLS0tLS0tLS0tIGFycmFuZ2UgcGxvdHMgdG9nZXRoZXIgLS0tLS0tLS0tIwpgYGB7cn0KZ2dhcnJhbmdlKG1jX2FsbF9maXRuZXNzLnBsLCBtY19wYXJ0aXRpb24ucGwsIGFsaWduID0gImgiLCB3aWR0aHMgPSBjKDEuMywgMiksIGxhYmVscyA9IGMoIkEiLCAiQiIpKQpnZ3NhdmUodW5pdHMgPSAicHgiLCBkcGkgPSAzMDAsIHdpZHRoID0gMjI1MCwgaGVpZ2h0ID0gMTUwMCwgZmlsZW5hbWUgPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9tY19maXRuZXNzX3BhcnRpdGlvbi50aWZmIiksIGJnID0gIndoaXRlIiwgc2NhbGUgPSAxLjIpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBTdXBwbGVtZW50YXJ5IGZpZ3VyZSBvbiBNQyBwb3N0ZXJpb3IgcGFyYW1ldGVyIGRpc3RyaWJ1dGlvbgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIyBQcm9jZXNzIGRhdGEKYGBge3J9CiMjIG1hbnVhbGx5IG1ha2UgZGF0YWZyYW1lIGNvbnRhaW5pbmcgdGhlIHBhcmFtZXRlciB2YWx1ZXMgdXNlZCBpbiB0aGUgZGV0ZXJtaW5pc3RpYyBtb2RlbApwYXJfZGV0LmRmIDwtIGRhdGEuZnJhbWUoCiAgcGFyYW1ldGVyID0gYygicmhvIiwgInBoaV9OMSIsICJwaGlfTjIiLCAiaW90YV9OMSIsICJpb3RhX04yIiwgImJ1cnN0IiksCiAgZGV0ZXJtaW5pc3RpYyA9IGMoMi42MjcxNTZlLTAxLCAzLjUyMDU5MWUtMDIsIDUuNTA4NDIwZSswMiwgMS42NjkyMzRlKzAxLCA4LjQzMTc4NWUtMDEsIDUuNzIxMDAwZSswMCApCikKCiMjIG1ha2UgaW50byBsb25nIGZvcm1hdApwb3N0ZXJpb3IubG9uZyA8LSBwb3N0ZXJpb3IuZGYgJT4lIAogIGZpbHRlcihpZCAlaW4lIHNlcSgwLDEwMDAsMSkpICU+JSAKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKC1pZCwgbmFtZXNfdG8gPSAicGFyYW1ldGVyIikgJT4lIAogIGxlZnRfam9pbihwYXJfZGV0LmRmLCBieSA9ICJwYXJhbWV0ZXIiKSAlPiUgIyMgYWRkIGluIHRoZSBkZXRlcm1pbmlzdGljIHBhcmFtZXRlciB2YWx1ZXMKICBtdXRhdGUobGFiZWwgPSBjYXNlX3doZW4oCiAgICBwYXJhbWV0ZXIgPT0gInJobyIgfiAiUkJDIHJlcGxlbmlzaG1lbnQgKM+BKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBoaV9OMSIgfiAiSGFsZi1saWZlIGluZGlzICjPlW4pIiwKICAgIHBhcmFtZXRlciA9PSAicGhpX04yIiB+ICJIYWxmLWxpZmUgdGFyZ2V0ZWQgKM+VdykiLAogICAgcGFyYW1ldGVyID09ICJpb3RhX04xIiB+ICJBY3RpdmF0aW9uIGluZGlzICjPiG4pIiwKICAgIHBhcmFtZXRlciA9PSAiaW90YV9OMiIgfiAiQWN0aXZhdGlvbiB0YXJnZXRlZCAoz4h3KSIsCiAgICBwYXJhbWV0ZXIgPT0gImJ1cnN0IiB+ICJCdXJzdCBzaXplICjOsikiCiAgKSkgIyMgcmVuYW1lIHBhcmFtdGVyIHZhbHVlcwoKYGBgCgojIyBwbG90CmBgYHtyfQojIyBwb3J0aW9uIG9mIHBhcmFtZXRlciB2YWx1ZXMgdGhhdCBkb2VzIG5vdCBuZWVkIGxvZy10cmFuc2Zvcm1pbmcKcG9zdGVyaW9yXzEucGwgPC0gZ2dwbG90KHBvc3Rlcmlvci5sb25nICU+JSBmaWx0ZXIoIXBhcmFtZXRlciAlaW4lIGMoInBoaV9OMSIsICJwaGlfTjIiLCAiaW90YV9OMSIpKSkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IHZhbHVlKSwgZmlsbCA9ICJncmV5IikgKwogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBkZXRlcm1pbmlzdGljKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGZhY2V0X3dyYXAofmxhYmVsLCBzY2FsZXMgPSAiZnJlZSIpICsKICBsYWJzKHggPSAiIiwgeSA9ICJEZW5zaXR5IikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKCiMjIFBsb3RzIHdoZXJlIHRoZSB4LWF4aXMgc2hvdWxkIGJlIGxvZy10cmFuc2Zvcm1lZCBzbyBpdCBpcyBlYXNpZXIgdG8gcmVhZApwb3N0ZXJpb3JfMi5wbCA8LSBnZ3Bsb3QocG9zdGVyaW9yLmxvbmcgJT4lIGZpbHRlcihwYXJhbWV0ZXIgJWluJSBjKCJwaGlfTjEiLCAicGhpX04yIiwgImlvdGFfTjEiKSkpICsKICBnZW9tX2RlbnNpdHkoYWVzKHggPSB2YWx1ZSksIGZpbGwgPSAiZ3JleSIpICsKICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gZGV0ZXJtaW5pc3RpYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBmYWNldF93cmFwKH5sYWJlbCwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh4ID0gIlZhbHVlIiwgeSA9ICJEZW5zaXR5IikgKwogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cxMCIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgojIyBwbG90IHRvZ2V0aGVyCmdnYXJyYW5nZShwb3N0ZXJpb3JfMS5wbCwgcG9zdGVyaW9yXzIucGwsIG5jb2wgPSAxLCBhbGlnbiA9ICJodiIpCiAgCiMjIHNhdmUKZ2dzYXZlKHVuaXRzID0gInB4IiwgZHBpID0gMzAwLCB3aWR0aCA9IDIwMDAsIGhlaWdodCA9IDEzMDAsIGZpbGVuYW1lID0gaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvcG9zdGVyaW9yLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIyBSYW5rIHBsb3Qgb2YgcGFyYXNpdGUgZml0bmVzcyB3aXRoIG9ubHkgb25lIHBhcmFtZXRlciB2YXJ5aW5nCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMjIFByb2Nlc3MgZGF0YQpgYGB7cn0KIyMgc2ltcGxlciB2ZXJzaW9uIG9mIGxvbmcgdGFibGUgY29udGFpbmluZyBmaXRuZXNzIG9mIHBhcmFzaXRlcyB3aGVuIHZhcmlvdXMgcGFyYW1ldGVyIGlzIHBlcnR1YmVkCm1jX3NpbmdsZV9maXRuZXNzLmxvbmcyIDwtIG1jX3NpbmdsZV9maXRuZXNzLmRmICU+JSAKICBmaWx0ZXIoaXRlciAlaW4lIHNlcSgwLDEwMDAsMSkpICU+JSAjIyBmaWx0ZXIgb3V0IGl0ZXIgPSAxLCB3aGljaCBkb2VzIG5vdCBoYXZlIHZhcmlhdGlvbgogIGRpc3RpbmN0KGlkLCBpdGVyLCAua2VlcF9hbGwgPSBUKSAlPiUgIyMgZW5zdXJlIHRoZXJlIGFyZSBub3QgcmVwbGljYXRlcwogIGZpbHRlcihpdGVyICE9IDEpICU+JSAKICBkcGx5cjo6c2VsZWN0KGlkLCBtYXhfZml0bmVzc19yaG8sIG1heF9maXRuZXNzX2JldGEsIG1heF9maXRuZXNzX3BzaW4sIG1heF9maXRuZXNzX3BzaXcsIG1heF9maXRuZXNzX3BoaW4sIG1heF9maXRuZXNzX3BoaXcsIGl0ZXIpICU+JSAjIGtlZXAgb25seSBmaXRuZXNzIGFuZCBhc3NvY2lhdGVkIGxhYmVscwogIHRpZHlyOjpwaXZvdF9sb25nZXIoLWMoImlkIiwgIml0ZXIiKSkgJT4lICMjIG1ha2UgbG9uZwogIG11dGF0ZShwYXJhbWV0ZXIgPSBnc3ViKCJtYXhfZml0bmVzc18iLCAiIiwgbmFtZSkpICU+JSAKICBtdXRhdGUoCiAgICAgICAgIHBhcmFtZXRlcl9sYWJlbCA9IGNhc2Vfd2hlbiggIyMgcmVjb2RlIHBhcmFtZXRlciB2YWx1ZXMKICAgIHBhcmFtZXRlciA9PSAicmhvIiB+ICJSQkMgcmVwbGVuaXNobWVudCAoz4EpIiwKICAgIHBhcmFtZXRlciA9PSAicGhpbiIgfiAiSGFsZi1saWZlIGluZGlzICjPlW4pIiwKICAgIHBhcmFtZXRlciA9PSAicGhpdyIgfiAiSGFsZi1saWZlIHRhcmdldGVkICjPlXcpIiwKICAgIHBhcmFtZXRlciA9PSAicHNpbiIgfiAiQWN0aXZhdGlvbiBpbmRpcyAoz4huKSIsCiAgICBwYXJhbWV0ZXIgPT0gInBzaXciIH4gIkFjdGl2YXRpb24gdGFyZ2V0ZWQgKM+IdykiLAogICAgcGFyYW1ldGVyID09ICJiZXRhIiB+ICJCdXJzdCBzaXplICjOsikiLAogICksCiAgZml0bmVzc19ub3JtID0gdmFsdWUvOS44ODM2MDIpICU+JSAgIyMgbm9ybWFsaXplIGZpdG5lc3MgdmFsdWUKICBsZWZ0X2pvaW4oZXpfbGFiZWwsIGJ5ID0gYygiaWQiKSkgJT4lIAogIGxlZnRfam9pbigKICAgIHJiaW5kKAogICAgc2VsZWN0KHNpX29wdC5kZiwgaWQsIGZpdG5lc3NfZGV0ID0gZml0bmVzc18yMCkgLCMjIGdldCBkZXRlcm1pbmlzdGljIGZpdG5lc3MgKHNpbmdsZSBjdWUpIAogICAgZHVhbF9jdWVfZl9maW5hbC5kZiAlPiUgbXV0YXRlKGlkID0gcGFzdGUwKGlkLCAiLSIsIGlkX2IpKSAlPiUgc2VsZWN0KGlkLCBmaXRuZXNzX2RldCA9IGZpdG5lc3MpICMjIGdldCBkZXRlcm1pbmlzdGljIGZpdG5lc3MgKGR1YWwgY3VlKQogICAgKSwKICAgIGJ5ID0gImlkIikgJT4lIAogIG11dGF0ZShmaXRuZXNzX2RldF9ub3JtID0gZml0bmVzc19kZXQvOS44ODM2MDIpICMjIG5vcm1hbGl6ZSBkZXRlcm1pbmlzdGljIGZpdG5lc3MKCm1jX3NpbmdsZV9maXRuZXNzLmxvbmcyICU+JSBkaXN0aW5jdChpZCwgZml0bmVzc19kZXQpCgojIyByZW9yZGVyIHBhcmFtZXRlcgptY19zaW5nbGVfZml0bmVzcy5sb25nMiRwYXJhbWV0ZXJfbGFiZWwgPC0gZmFjdG9yKG1jX3NpbmdsZV9maXRuZXNzLmxvbmcyJHBhcmFtZXRlcl9sYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJCdXJzdCBzaXplICjOsikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJCQyByZXBsZW5pc2htZW50ICjPgSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhhbGYtbGlmZSBpbmRpcyAoz5VuKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGFsZi1saWZlIHRhcmdldGVkICjPlXcpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY3RpdmF0aW9uIGluZGlzICjPiG4pIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY3RpdmF0aW9uIHRhcmdldGVkICjPiHcpIikpCgojIyBnZXQgbWVkaWFuIGZpdG5lc3MgdmFsdWVzIChub3JtYWxpemVkKQptY19zaW5nbGVfZml0bmVzcy5zdW0yIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmcyICU+JSAKICBncm91cF9ieShzaG9ydF9sYWJlbCwgaWQsIHBhcmFtZXRlcl9sYWJlbCwgcGFyYW1ldGVyKSAlPiUgCiAgc3VtbWFyaXplKG1lZGlhbiA9IG1lZGlhbihmaXRuZXNzX25vcm0pLAogICAgICAgICAgICBtZWFuID0gbWVhbihmaXRuZXNzX25vcm0pLAogICAgICAgICAgICBnZW9fbWVhbiA9IGV4cChtZWFuKGxvZyhmaXRuZXNzX25vcm0pKSksCiAgICAgICAgICAgIGZpdG5lc3NfZGV0X25vcm0gPSB1bmlxdWUoZml0bmVzc19kZXRfbm9ybSkpICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KG1jX2FsbF9maXRuZXNzX3N1bS5kZiwgc2hvcnRfbGFiZWwsIGdlb19tZWFuX2FsbCA9IGdlb21fbWVhbiksIGJ5ID0gInNob3J0X2xhYmVsIikgIyMgZ2V0IGdlb21ldHJpYyBtZWFuIG9mIHdoZW4gYWxsIHBhcmFtZXRlciBhcmUgdmFyaWVkCgojIGxlZnQgam9pbiBnZW9tZXRyaWMgZml0bmVzcyB0byBsb25nIGRmIGZvciByYW5raW5nIHB1cnBvc2VzCm1jX3NpbmdsZV9maXRuZXNzLmxvbmcyIDwtIG1jX3NpbmdsZV9maXRuZXNzLmxvbmcyICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KG1jX3NpbmdsZV9maXRuZXNzLnN1bTIsIGlkLHBhcmFtZXRlcl9sYWJlbCwgbWVkaWFuLCBtZWFuLCBnZW9fbWVhbiksIGJ5ID0gYygiaWQiLCAicGFyYW1ldGVyX2xhYmVsIiwgInNob3J0X2xhYmVsIikpIAoKYGBgCgojIHBsb3QKbm90ZSB0aGF0IGl0IGlzIHF1aXRlIGEgaGFzc2VsIHRvIHJlb3JkZXIgY3VlcyB3aGVuIGZhY2V0dGluZy4gSGVuY2UsIEkgd2lsbCBqdXN0IHBsb3Qgb3V0IGFsbCA2IHBsb3RzIGluZGl2aWR1YWxseQpgYGB7cn0KIyBzcGxpdCBkYXRhYmFzZSBieSBwYXJhbWV0ZXIKbWNfc2luZ2xlX2ZpdG5lc3NfbG9uZzIubHMgPC0gc3BsaXQobWNfc2luZ2xlX2ZpdG5lc3MubG9uZzIsIG1jX3NpbmdsZV9maXRuZXNzLmxvbmcyJHBhcmFtZXRlcl9sYWJlbCkKbWNfc2luZ2xlX2ZpdG5lc3Nfc3VtMi5scyA8LSBzcGxpdChtY19zaW5nbGVfZml0bmVzcy5zdW0yLCBtY19zaW5nbGVfZml0bmVzcy5zdW0yJHBhcmFtZXRlcl9sYWJlbCkKCiMgcGxvdCBvdXQgaW5kaXZpZHVhbCBnZ3Bsb3QgCm1jX3NpbmdsZV9wbHQubHMgPC0gbGlzdCgpCmZvcihpIGluIHNlcSgxLDYsMSkpewogbWNfc2luZ2xlX3BsdC5sc1tbaV1dIDwtIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihkYXRhID0gbWNfc2luZ2xlX2ZpdG5lc3NfbG9uZzIubHNbW2ldXSwKICAgICAgICAgICAgICBhZXMoeCA9IGZjdF9yZW9yZGVyKHNob3J0X2xhYmVsLCBnZW9fbWVhbiwgLmRlc2MgPSBUKSwgeSA9IGZpdG5lc3Nfbm9ybSksIAogICAgICAgICAgICAgIGZpbGwgPSAiZ3JleSIsIHRyaW0gPSBULCBjb2xvciA9ICJncmV5IikgKwogIGdlb21fcG9pbnQoZGF0YSA9ICBtY19zaW5nbGVfZml0bmVzc19zdW0yLmxzW1tpXV0sCiAgICAgICAgICAgICBhZXMoeCA9IHNob3J0X2xhYmVsLCB5ID0gbWVhbiwgc2hhcGUgPSAiTWVhbiIsIGNvbG9yID0gIk1lYW4iKSwgc2l6ZSA9IDIpICsKICBnZW9tX3BvaW50KGRhdGEgPSAgbWNfc2luZ2xlX2ZpdG5lc3Nfc3VtMi5sc1tbaV1dLAogICAgICAgICAgICAgYWVzKHggPSBzaG9ydF9sYWJlbCwgeSA9IGdlb19tZWFuLCBzaGFwZSA9ICJHZW9tZXRyaWMgbWVhblxuKHNpbmdsZSBwYXJhbWV0ZXIpIiwgY29sb3IgPSAiR2VvbWV0cmljIG1lYW5cbihzaW5nbGUgcGFyYW1ldGVyKSIpLCBzaXplID0gMikgKwogIGdlb21fcG9pbnQoZGF0YSA9ICBtY19zaW5nbGVfZml0bmVzc19zdW0yLmxzW1tpXV0sCiAgICAgICAgICAgICBhZXMoeCA9IHNob3J0X2xhYmVsLCB5ID0gZml0bmVzc19kZXRfbm9ybSwgc2hhcGUgPSAiRGV0ZXJtaW5pc3RpYyIsIGNvbG9yID0gIkRldGVybWluaXN0aWMiKSwgc2l6ZSA9MikgKwogICBnZW9tX3BvaW50KGRhdGEgPSAgbWNfc2luZ2xlX2ZpdG5lc3Nfc3VtMi5sc1tbaV1dLAogICAgICAgICAgICAgYWVzKHggPSBzaG9ydF9sYWJlbCwgeSA9IGdlb19tZWFuX2FsbCwgc2hhcGUgPSAiR2VvbWV0cmljIG1lYW5cbihhbGwgcGFyYW1ldGVycykiLCBjb2xvciA9ICJHZW9tZXRyaWMgbWVhblxuKGFsbCBwYXJhbWV0ZXJzKSIpLCBzaXplID0yKSArCiAgbGFicyh4ID0gIkN1ZShzKSIsIHkgPSAiTm9ybWFsaXplZCBmaXRuZXNzIiwgc2hhcGUgPSAiTGVnZW5kIiwgY29sb3IgPSAiTGVnZW5kIiwKICAgICAgIHN1YnRpdGxlID0gdW5pcXVlKG1jX3NpbmdsZV9maXRuZXNzX3N1bTIubHNbW2ldXSRwYXJhbWV0ZXJfbGFiZWwpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgIiM3ODVFRjAiLCIjNzg1RUYwIiwgIiNGRkIwMDAiKSkgKwogICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxNiwgMiwgMTcsIDE1KSkrCiAgIHlsaW0oMCwgMS4yKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpCiAgICAgICAgIAp9CgojIyBhcnJhbmdlIHRvZ2V0aGVyCmdnYXJyYW5nZShwbG90bGlzdCA9IG1jX3NpbmdsZV9wbHQubHMsIGNvbW1vbi5sZWdlbmQgPSBULCBuY29sID0gMywgbnJvdyA9IDIsIGFsaWduID0gImh2IikKCmdnc2F2ZSh1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyNTAwLCBoZWlnaHQgPSAyMDAwLCBmaWxlbmFtZSA9IGhlcmUoImNvZGVfcmVwb3NpdG9yeS9maWd1cmVzL21jX3NpbmdsZV9maXRuZXNzLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEpCmBgYAoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgVW5kZXJzdGFuZGluZyB0aGUgbWVjaGFuaXNtIGJlaGluZCByb2J1c3RuZXNzCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojLS0tLS0tLS1DYWxjdWxhdGluZyBkZXZpYXRpb25zIGJldHdlZW4gY3IgYW5kIGZpdG5lc3MtLS0tLS0tLS0tIwojIFByb2Nlc3Mgc2luZ2xlIHBhcmFtZXRlciBkeW5hbWljcyBkYXRhCmBgYHtyfQojIGdldCBsaXN0IG9mIGZpbGVzCm1jX3NpbmdsZV9keW4ubHMgPC0gbGlzdC5maWxlcyhoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZHluIiksIHBhdHRlcm4gPSAiKi5wYXJxdWV0IiwgZnVsbC5uYW1lcyA9IFQpCgojIHJlYWQgaW4gYWxsIG9mIHRoZSBmaWxlcywgZmlsdGVyaW5nIG9ubHkgZm9yIGNvbnZlcnNpb24gcmF0ZQptY19zaW5nbGVfZHluX2NyLmxzIDwtIG1jbGFwcGx5KG1jX3NpbmdsZV9keW4ubHMsIGZ1bmN0aW9uKHgpewogIGRmIDwtIHJlYWRfcGFycXVldCh4KQogIAogICMjIGZpbHRlciB0byByZXRhaW4gb25seSBjcgogIGRmX2YgPC0gZGYgJT4lIGZpbHRlcih2YXJpYWJsZSA9PSAiY3IiKQogIHJldHVybihkZl9mKQp9LCBtYy5jb3JlcyA9IDYpCgojIGNvbWJpbmUKbWNfc2luZ2xlX2R5bl9jci5kZiA8LSBkby5jYWxsKHJiaW5kLCBtY19zaW5nbGVfZHluX2NyLmxzKQoKIyB3cml0ZQp3cml0ZV9wYXJxdWV0KG1jX3NpbmdsZV9keW5fY3IuZGYsIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX3NpbmdsZV9keW5fY3IucGFycXVldCIpKQptY19zaW5nbGVfZHluX2NyLmRmIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZHluX2NyLnBhcnF1ZXQiKSkKCiMgY2hlY2sgIyBvZiByZWNvcmRzCm1jX3NpbmdsZV9keW5fY3IuZGYgJT4lIGdyb3VwX2J5KGlkKSAlPiUgdGFsbHkoKQpgYGAKCiMgUHJvY2VzcyBkZXRlcm1pbmlzdGljIGNvbnZlcnNpb24gcmF0ZSB0aW1lIHNlcmllcyBkeW5hbWljCmBgYHtyfQojIHNpbmdsZSBjdWUgZHluYW1pY3MgKGNvbnZlcnNpb24gcmF0ZSBvbmx5KQpzaV9jci5kZiA8LSBzaV9keW4uZGYgJT4lCiAgZmlsdGVyKHZhcmlhYmxlID09ICJjciIpICU+JSAgIyMgZ2V0IGNvcnJlc3BvbmRpbmcgdGltZSBzdGVwcyAoMC4wMSkgYW5kIGNyIG9ubHkKICBzZWxlY3QoaWQsIHRpbWUsIGNyX2RldCA9IHZhbHVlKQoKIyBkdWFsIGN1ZSBkeW5hbWljcyAoY29udmVyc2lvbiByYXRlIG9ubHkpCmR1YWxfY3IuZGYgPC0gZHVhbF9jdWVfZHluLmRmICU+JSAKICBmaWx0ZXIodmFyaWFibGUgPT0gImNyIikgJT4lIAogIG11dGF0ZShpZCA9IHBhc3RlMChpZCwgIi0iLCBpZF9iKSkgJT4lIAogIHNlbGVjdChpZCwgdGltZSwgY3JfZGV0ID0gdmFsdWUpCmBgYAoKIyBDYWxjdWxhdGUgZGV2aWF0aW9uIGluIGNyCmBgYHtyfQojIGxlZnQgam9pbiAKbWNfc2luZ2xlX2R5bl9jcl9qb2luLmRmIDwtIG1jX3NpbmdsZV9keW5fY3IuZGYgJT4lIAogIGxlZnRfam9pbihyYmluZCgKICAgIHNpX2NyLmRmLCBkdWFsX2NyLmRmCiAgKSwgYnkgPSBjKCJ0aW1lIiwgImlkIikpCgojIGdldCBhYnNvbHVhdGUgZGV2aWF0aW9uIAptY19zaW5nbGVfZHluX2NyX2pvaW4uZGZfcCA8LSBtY19zaW5nbGVfZHluX2NyX2pvaW4uZGYgJT4lIAogIGZpbHRlcih0aW1lICE9IDApICU+JSAjIyBnZXQgcmlkIG9mIHRpbWUgPSAwIHdoZXIgY3IgaXMgTkEKICBtdXRhdGUoZGlmZiA9IGFicyhjcl9kZXQtdmFsdWUpKSAjIyBjYWxjdWxhdGUgYWJzb2x1dGUgZGV2aWF0aW9uCndyaXRlX3BhcnF1ZXQobWNfc2luZ2xlX2R5bl9jcl9qb2luLmRmX3AsIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX3NpbmdsZV9keW5fY3Jfam9pbi5wYXJxdWV0IikpCm1jX3NpbmdsZV9keW5fY3Jfam9pbi5kZl9wIDwtIHJlYWRfcGFycXVldChoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZHluX2NyX2pvaW4ucGFycXVldCIpKQoKIyBjaGVjayBmb3IgTkFzCm1jX3NpbmdsZV9keW5fY3Jfam9pbi5kZl9wICU+JSBmaWx0ZXIoaXMubmEoZGlmZikpCgojIGNoZWNrICMgb2Ygc2FtcGxlcwptY19zaW5nbGVfZHluX2NyX2pvaW4uZGZfcCAlPiUgZ3JvdXBfYnkoaWQsIHBhcmFtZXRlcikgJT4lIHRhbGx5KCkKCiMgY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcwptY19zaW5nbGVfZHluX2NyLnN1bSA8LSBtY19zaW5nbGVfZHluX2NyX2pvaW4uZGZfcCAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShpZCwgcGFyYW1ldGVyKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW5fY3JfZGlmZiA9IG1lYW4oZGlmZiksCiAgICAgICAgICAgIG1lZGlhbl9jcl9kaWZmID0gbWVkaWFuKGRpZmYpLAogICAgICAgICAgICBzZF9jcl9kaWZmID0gc2QoZGlmZiksCiAgICAgICAgICAgIG1hZF9jcl9kaWZmPSBtYWQoZGlmZikpCgp3cml0ZS5jc3YobWNfc2luZ2xlX2R5bl9jci5zdW0sIGhlcmUoImNvZGVfcmVwb3NpdG9yeS9kYXRhL21jX3NpbmdsZV9keW5fY3Jfc3VtLmNzdiIpKQptY19zaW5nbGVfZHluX2NyLnN1bSA8LSByZWFkLmNzdihoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZGF0YS9tY19zaW5nbGVfZHluX2NyX3N1bS5jc3YiKSkKYGBgCgojIGNhbGN1bGF0ZSBsb3NzIG9mIGZpdG5lc3MgZHVlIHRvIHZhcmlhdGlvbgpgYGB7cn0KbWNfc2luZ2xlX2R5bl9jci5zdW1fZml0bmVzcyA8LSBtY19zaW5nbGVfZml0bmVzcy5zdW0yICU+JSAKICBtdXRhdGUoZ2VvX21lYW5fZGlmZiA9IGdlb19tZWFuLWZpdG5lc3NfZGV0X25vcm0pICU+JSAKICBsZWZ0X2pvaW4obWNfc2luZ2xlX2R5bl9jci5zdW0sIGJ5ID0gYygiaWQiLCAicGFyYW1ldGVyIikpCmBgYAoKIyBpc29sYXRlIGV4YW1wbGVzIG9mIHJvYnVzdCBhbmQgbm9uLXJvYnVzdCBjdWVzCmBgYHtyfQojIyByb2J1c3QgY3VlIChJIGxvZykKbWNfc2luZ2xlX2R5bl9jcl9qb2luLmRmX3Bfcm9idXN0IDwtIG1jX3NpbmdsZV9keW5fY3Jfam9pbi5kZl9wICU+JSAKICBmaWx0ZXIoaWQgPT0gIklfbG9nIiAmIHBhcmFtZXRlciAlaW4lIGMoImJldGEiLCAicHNpbiIpICYgcm93X251bWJlcigpICUlIDEwID09IDApICU+JSAKICBtdXRhdGUocGFyYW1ldGVyX2xhYmVsID0gY2FzZV93aGVuKAogICAgcGFyYW1ldGVyID09ICJwc2luIiB+ICJBY3RpdmF0aW9uIGluZGlzICjPiG4pIiwKICAgIHBhcmFtZXRlciA9PSAiYmV0YSIgfiAiQnVyc3Qgc2l6ZSAozrIpIgogICkpCiAgCiMjIG5vbi1yb2J1c3QgY3VlIChSIGFuZCBJK0lnIGxvZykKbWNfc2luZ2xlX2R5bl9jcl9qb2luLmRmX3Bfbm90cm9idXN0IDwtIG1jX3NpbmdsZV9keW5fY3Jfam9pbi5kZl9wICU+JSAKICBmaWx0ZXIoaWQgPT0gIlJfbm9uZS1JK0lnX2xvZyIgJiBwYXJhbWV0ZXIgJWluJSBjKCJiZXRhIiwgInBzaW4iKSAmIHJvd19udW1iZXIoKSAlJSAxMCA9PSAwKSAlPiUgCiAgbXV0YXRlKHBhcmFtZXRlcl9sYWJlbCA9IGNhc2Vfd2hlbigKICAgIHBhcmFtZXRlciA9PSAicHNpbiIgfiAiQWN0aXZhdGlvbiBpbmRpcyAoz4huKSIsCiAgICBwYXJhbWV0ZXIgPT0gImJldGEiIH4gIkJ1cnN0IHNpemUgKM6yKSIKICApKQpgYGAKCiMgcGxvdCBjciB0aW1lIHNlcmllcwpgYGB7cn0KbWNfc2luZ2xlX2NyX2R5bi5wbHQxIDwtIGdncGxvdChtY19zaW5nbGVfZHluX2NyX2pvaW4uZGZfcF9yb2J1c3QpICsKICBnZW9tX2xpbmUoYWVzKHggPSB0aW1lLCB5ID0gdmFsdWUsIGdyb3VwID0gaXRlciksIGFscGhhID0gMC4wMSkgKwogIGdlb21fbGluZShhZXMoeCA9IHRpbWUsIHkgPSBjcl9kZXQsIGdyb3VwID0gaXRlciksIGFscGhhID0gMSwgY29sb3IgPSAiIzc4NUVGMCIpICsKICBmYWNldF93cmFwKH5wYXJhbWV0ZXJfbGFiZWwpICsKICB5bGltKDAsIDEpICsKICBsYWJzKHggPSAiIiwgeSA9ICJDb252ZXJzaW9uIHJhdGUiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkKCm1jX3NpbmdsZV9jcl9keW4ucGx0MiA8LSBnZ3Bsb3QobWNfc2luZ2xlX2R5bl9jcl9qb2luLmRmX3Bfbm90cm9idXN0KSArCiAgZ2VvbV9saW5lKGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlLCBncm91cCA9IGl0ZXIpLCBhbHBoYSA9IDAuMDEpICsKICBnZW9tX2xpbmUoYWVzKHggPSB0aW1lLCB5ID0gY3JfZGV0LCBncm91cCA9IGl0ZXIpLCBhbHBoYSA9IDEsIGNvbG9yID0gIiM3ODVFRjAiKSArCiAgZmFjZXRfd3JhcCh+cGFyYW1ldGVyX2xhYmVsKSArCiAgeWxpbSgwLCAxKSArCiAgbGFicyh4ID0gIlRpbWUgKGRheXMpIiwgeSA9ICJDb252ZXJzaW9uIHJhdGUiKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMgcGxvdCByZWxhdGlvbnNoaXAKYGBge3J9CiMgcmVsYXRpb25zaGlwIGJldHdlZW4gZml0bmVzcyB2cyByb2Jpc3RuZXNzCmdncGxvdChtY19zaW5nbGVfZHluX2NyLnN1bV9maXRuZXNzLCBhZXMoeCA9IGZpdG5lc3NfZGV0X25vcm0sIHkgPSBnZW9fbWVhbl9kaWZmLCBjb2xvciA9IGZpdG5lc3NfZGV0X25vcm0pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMi41LCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhPTAuOCkgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlcl9sYWJlbCwgc2NhbGVzID0gImZyZWUiKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGxhYnMoeCA9ICJEZXRlcm1pbmlzdGljIG1vZGVsIGZpdG5lc3MiLCB5ID0gIk5vcm1hbGl6ZWQgZml0bmVzcyBkaWZmZXJlbmNlXG4oU2luZ2xlLURldGVybWluaXN0aWMpIiwgY29sb3IgPSAiRGV0ZXJtaW5pc3RpY1xubW9kZWwgZml0bmVzcyIpICsKICB0aGVtZV9idygpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCgojIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNvbnZlcnNpb24gcmF0ZSBkaWZmZXJlbmNlIGFuZCBkaWZmZXJlbmNlIGluIGZpdG5lc3MgCmdncGxvdChtY19zaW5nbGVfZHluX2NyLnN1bV9maXRuZXNzLCBhZXMoeCA9IG1lZGlhbl9jcl9kaWZmLCBnZW9fbWVhbl9kaWZmLCBjb2xvciA9IGZpdG5lc3NfZGV0X25vcm0pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMi41LCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhPTAuOCkgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlcl9sYWJlbCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICB5bGltKE5BLCAwLjAyKSArCiAgbGFicyh4ID0gIk1lZGlhbiBjb252ZXJzaW9uIHJhdGUgZGlmZmVyZW5jZSIsIHkgPSAiTm9ybWFsaXplZCBmaXRuZXNzIGRpZmZlcmVuY2VcbihTaW5nbGUtRGV0ZXJtaW5pc3RpYykiLCBjb2xvciA9ICJEZXRlcm1pbmlzdGljXG5tb2RlbCBmaXRuZXNzIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmdnc2F2ZShoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9tY19zaW5nbGVfY3JfcmVsX2ludC50aWZmIiksIHVuaXRzID0gInB4IiwgZHBpID0gMzAwLCB3aWR0aCA9IDI1NTAsIGhlaWdodCA9IDEyMDAsIGJnID0gIndoaXRlIiwgc2NhbGUgPSAxKQoKIyBpbnRlcm5hbCBjb2RlIHRvIGdldCBwb3NpdGlvbnMgb2YgSSBsb2cgYW5kIFIgYW5kIEkrSWcgbG9nCmdncGxvdChtY19zaW5nbGVfZHluX2NyLnN1bV9maXRuZXNzLCBhZXMoeCA9IG1lZGlhbl9jcl9kaWZmLCBnZW9fbWVhbl9kaWZmLCBjb2xvciA9IGZpdG5lc3NfZGV0X25vcm0pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMi41LCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIGFscGhhPTAuOCkgKwogIGdlb21fdGV4dChkYXRhID0gbWNfc2luZ2xlX2R5bl9jci5zdW1fZml0bmVzcyAlPiUgZmlsdGVyKGlkICVpbiUgYygiSV9sb2ciLCAiUl9ub25lLUkrSWdfbG9nIikpLCBhZXMoeCA9IG1lZGlhbl9jcl9kaWZmLCB5ID1nZW9fbWVhbl9kaWZmLCBsYWJlbCA9IGlkKSkgKwogIGZhY2V0X3dyYXAofnBhcmFtZXRlcl9sYWJlbCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICB5bGltKE5BLCAwLjAyKSArCiAgbGFicyh4ID0gIk1lZGlhbiBjb252ZXJzaW9uIHJhdGUgZGlmZmVyZW5jZSIsIHkgPSAiTm9ybWFsaXplZCBmaXRuZXNzIGRpZmZlcmVuY2VcbihTaW5nbGUtRGV0ZXJtaW5pc3RpYykiLCBjb2xvciA9ICJEZXRlcm1pbmlzdGljXG5tb2RlbCBmaXRuZXNzIikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgojIHBsb3QgdG9nZXRoZXIKYGBge3J9CiMgY29udmVyc2lvbiByYXRlIGR5bmFtaWMKZ2dhcnJhbmdlKG1jX3NpbmdsZV9jcl9keW4ucGx0MSwgbWNfc2luZ2xlX2NyX2R5bi5wbHQyLCBucm93ID0gMiwgYWxpZ24gPSAidiIsIGxhYmVscyA9IGMoIkIiLCAiQyIpKQpnZ3NhdmUoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2ZpZ3VyZXMvbWNfc2luZ2xlX2NyX2R5bl9pbnQudGlmZiIpLCB1bml0cyA9ICJweCIsIGRwaSA9IDMwMCwgd2lkdGggPSAyNTUwKigyLzMpLCBoZWlnaHQgPSAxMDAwLCBiZyA9ICJ3aGl0ZSIsIHNjYWxlID0gMSkKYGBgCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0jCiMgSW1wYWN0IG9mIGN1ZSBwZXJjZXB0aW9uIG9uIHZpcnVsZW5jZQojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojLS0tLS0tLS0tIERpdmlkIGR5bmFtaWNzIGRhdGEgaW50byBoaWdoIGFuZCBsb3cgcGVyZm9ybWluZyAtLS0tLS0tLS0tLS0tLS0jCiMgU2luZ2xlIGN1ZSAKYGBge3J9CiMgY2xhc3NpZnkgdG9wIDQgcGVyZm9ybWluZyBzaW5nbGUgY3VlcyBhcyAiaGlnaCBwZXJmb3JtaW5nIgp0b3BfNF9zaW5nbGVfY3VlIDwtIHNpX29wdC5kZiAlPiUgCiAgZmlsdGVyKGN1ZSAhPSAidCIpICU+JSAKICBzbGljZV9tYXgobiA9IDQsIG9yZGVyX2J5ID0gZml0bmVzc18yMCkgJT4lIAogIG11dGF0ZShzaG9ydF9sYWJlbCA9IGdzdWIoIklcXCtJZyIsICJUb3RhbCBJIiwgc2hvcnRfbGFiZWwpKQoKIyBnZXQgd2lkZSBmb3JtYXQgZGF0YWZyYW1lLiBIZXJlLCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgUkJDIGFuZCB0b3RhbCBpUkJDIGNvdW50CnNpX2RjLmRmIDwtIHNpX2R5bl8zMC5kZiAlPiUgCiAgbXV0YXRlKHZhbHVlID0gYXMubnVtZXJpYyh2YWx1ZSkpICU+JSAKICBmaWx0ZXIodmFyaWFibGUgPT0gIkkiIHwgdmFyaWFibGUgPT0gIklnIiB8IHZhcmlhYmxlID09ICJSIikgJT4lIAogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdmFyaWFibGUsIHZhbHVlc19mcm9tID0gdmFsdWUpICU+JSAKICBtdXRhdGUodG90YWwgPSBJK0lnKQoKIyBzcGxpdCBpbnRvIHRvcCBhbmQgcG9vci1wZXJmb3JtaW5nIGN1ZXMKc2lfZGMuaGlnaCA8LSBzaV9kYy5kZiAlPiUgZmlsdGVyKGlkICVpbiUgdG9wXzRfc2luZ2xlX2N1ZSRpZCkKCiMgam9pbiBoaWdoIHBlcmZvcm1pbmcgd2l0aCBsYWJlbApzaV9kYy5oaWdoIDwtIHNpX2RjLmhpZ2ggJT4lIGxlZnRfam9pbihlel9sYWJlbCAlPiUgZGlzdGluY3QoaWQsIC5rZWVwX2FsbCA9IFQpLCBieSA9ICJpZCIpCgojIHJlb3JkZXIgY3VlcyBieSBkZXNjZW5kaW5nIGZpdG5lc3MKc2lfZGMuaGlnaCRzaG9ydF9sYWJlbCA8LSBmYWN0b3Ioc2lfZGMuaGlnaCRzaG9ydF9sYWJlbCwgdG9wXzRfc2luZ2xlX2N1ZSRzaG9ydF9sYWJlbCkKYGBgCgojIER1YWwgY3VlCmBgYHtyfQojIGNsYXNzaWZ5IHRvcCA0IHBlcmZvcm1pbmcgZHVhbCBjdWVzIGFzICJoaWdoIHBlcmZvcm1pbmciCnRvcF80X2R1YWxfY3VlIDwtIGR1YWxfY3VlX2ZfZmluYWwuZGYgJT4lIAogIG11dGF0ZShsYWJlbF9hbHQgPSBnc3ViKCJJXFwrSWciLCAiVG90YWwgSSIsIHBhc3RlKGxhYmVsLCAiJiIgLCBsYWJlbF9iKSkpICU+JSAKICBzbGljZV9tYXgobiA9IDQsIG9yZGVyX2J5ID0gZml0bmVzcykKCiMgZ2V0IHdpZGUgZm9ybWF0IGRhdGFmcmFtZS4gSGVyZSwgd2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIFJCQyBhbmQgdG90YWwgaVJCQyBjb3VudApkdWFsX2RjLmRmIDwtIGR1YWxfY3VlX2R5bl8zMC5kZiAlPiUgCiAgbXV0YXRlKGxhYmVsX2FsdCA9IGdzdWIoIklcXCtJZyIsICJUb3RhbCBJIiwgcGFzdGUobGFiZWwsICImIiAsIGxhYmVsX2IpKSkgJT4lICMjIGdldCBuZXcgbGFiZWwKICBmaWx0ZXIodmFyaWFibGUgPT0gIkkiIHwgdmFyaWFibGUgPT0gIklnIiB8IHZhcmlhYmxlID09ICJSIikgJT4lICMjIGdldCBkZXNpcmVkIHZhcmlhYmxlcwogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdmFyaWFibGUsIHZhbHVlc19mcm9tID0gdmFsdWUsIGlkX2NvbHMgPSBjKHRpbWUsIGxhYmVsX2FsdCkpICU+JQogIG11dGF0ZSh0b3RhbCA9IEkrSWcpCgojIHNwbGl0IGludG8gdG9wIGFuZCBwb29yLXBlcmZvcm1pbmcgY3VlcwpkdWFsX2RjLmhpZ2ggPC0gZHVhbF9kYy5kZiAlPiUgZmlsdGVyKGxhYmVsX2FsdCAlaW4lIHRvcF80X2R1YWxfY3VlJGxhYmVsX2FsdCkKCiMgcmVvcmRlciBjdWVzIGJ5IGRlc2NlbmRpbmcgZml0bmVzcwpkdWFsX2RjLmhpZ2gkbGFiZWxfYWx0IDwtIGZhY3RvcihkdWFsX2RjLmhpZ2gkbGFiZWxfYWx0LCB0b3BfNF9kdWFsX2N1ZSRsYWJlbF9hbHQpCmBgYAoKIy0tLS0tLS0tLS0tLUdldCByZWZlcmVuY2UgZHluYW1pY3MtLS0tLS0tLS0tLS0tLS0jCiMgdGhlIGlkZWFsIGR5bmFtaWMgKHRpbWUgYXMgY3VlLCBkZiA9IDkpCmBgYHtyfQpzb3VyY2UoaGVyZSgiY29kZV9yZXBvc2l0b3J5L2Z1bmN0aW9ucy9jaGFiYXVkaV9zaV9jbGVhbl9oaWdoLlIiKSkKdGltZV9oaWdoLmR5biA8LSBjaGFiYXVkaV9zaV9jbGVhbl9oaWdoKAogIHBhcmFtZXRlcnNfY3IgPSBjKDkuMTU0MzE0LCAgLTcuNTcwODI5LCAtMjIuNTA2NjM4ICwgIDMuMzgyNDA1ICwtMTMuNDUzNTE5ICwtMTcuMDExNDg1ICAsIDMuNjc4MTgxLCAtMTIuODUxODk1ICwtMjYuMTE1MTU4KSwKICBpbW11bml0eSA9ICJ0c3VrdXNoaSIsCiAgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfdHN1a3VzaGksCiAgdGltZV9yYW5nZSA9IHNlcSgwLCAzMCwgYnkgPSAxZS0zKSwKICBjdWVfcmFuZ2UgPSAgc2VxKDAsIDIwLCBieSA9IDFlLTMpLAogIGN1ZSA9ICJ0IiwKICBzb2x2ZXIgPSAidm9kZSIsCiAgZHluID0gVCkKCgojIGNoZWNrIGR5bmFtaWNzLiAKZ2dwbG90KHRpbWVfaGlnaC5keW4sIGFlcyh4ID0gdGltZSwgeSA9IHZhbHVlKSkgKwogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0iZnJlZSIpCgojIGdldCB0b3RhbCBJIGFuZCBSCnRpbWVfaGlnaC52aXIgPC0gdGltZV9oaWdoLmR5biAlPiUgCiBtdXRhdGUoc2hvcnRfbGFiZWwgPSAiVGltZSIpICU+JSAjIyBnZXQgbmV3IGxhYmVsCiAgZmlsdGVyKHZhcmlhYmxlID09ICJJIiB8IHZhcmlhYmxlID09ICJJZyIgfCB2YXJpYWJsZSA9PSAiUiIpICU+JSAjIyBnZXQgZGVzaXJlZCB2YXJpYWJsZXMKICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHZhcmlhYmxlLCB2YWx1ZXNfZnJvbSA9IHZhbHVlLCBpZF9jb2xzID0gYyh0aW1lLCBzaG9ydF9sYWJlbCkpICU+JQogIG11dGF0ZSh0b3RhbCA9IEkrSWcpCmBgYAoKIy0tLS0tLS0tLS0tLS1QbG90IGRpc2Vhc2UgY3VydmVzLS0tLS0tLS0tLS0tLS0tLS0jCiMgU2luZ2xlIGN1ZQpgYGB7cn0Kc2lfZGMucGx0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSB0aW1lX2hpZ2gudmlyICU+JSBmaWx0ZXIocm93X251bWJlcigpICUlIDEwMDAgPT0wKSwgYWVzKHggPSB0b3RhbCwgeSA9IFIsIGNvbG9yID0gc2hvcnRfbGFiZWwsIHNoYXBlID0gc2hvcnRfbGFiZWwpLCBzaXplID0gMykgKwogIGdlb21fcG9pbnQoZGF0YSA9IHNpX2RjLmhpZ2ggJT4lIGZpbHRlcihyb3dfbnVtYmVyKCkgJSUgMTAwMCA9PTApLCBhZXMoeCA9IHRvdGFsLCB5ID0gUiwgY29sb3IgPSBzaG9ydF9sYWJlbCwgc2hhcGUgPSBzaG9ydF9sYWJlbCksIHNpemUgPSAzKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSB0aW1lX2hpZ2gudmlyLCBhZXMoeD0gdG90YWwsIHkgPSBSLCBncm91cCA9IHNob3J0X2xhYmVsLCBjb2xvciA9IHNob3J0X2xhYmVsKSwgc2l6ZSA9IDEsIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSkgKwogICBnZW9tX3BhdGgoZGF0YSA9IHNpX2RjLmhpZ2gsIGFlcyh4PSB0b3RhbCwgeSA9IFIsIGdyb3VwID0gc2hvcnRfbGFiZWwsIGNvbG9yID0gc2hvcnRfbGFiZWwpLCBzaXplID0gMSwgYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGFuZ2xlID0gMTAsIGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCAiIzQ1NzViNCIsICIjZmRjYjQ0IiwgIiM5MWJmZGIiLCAiI2ZjOGQ1OSIsICJibGFjayIpLCBuYW1lID0gIkN1ZXMiKSAgKwogIGxhYnMoZmlsbCA9ICJDdWVzIiwgeCA9ICJUb3RhbCBpUkJDIiwgeSA9ICJSQkMiLCBjb2xvciA9ICJDdWVzIikgKwogIHRoZW1lX2NsYXNzaWMoKSsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCBzY2llbnRpZmljID0gVFJVRSwgYWNjdXJhY3kgPSAwLjEpKSArCiAgZ3VpZGVzKHNoYXBlID0gRikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpgYGAKCiMgZHVhbCBjdWUKYGBge3J9CmR1YWxfZGMucGx0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSB0aW1lX2hpZ2gudmlyICU+JSBmaWx0ZXIocm93X251bWJlcigpICUlIDEwMDAgPT0wKSwgYWVzKHggPSB0b3RhbCwgeSA9IFIsIGNvbG9yID0gc2hvcnRfbGFiZWwsIHNoYXBlID0gc2hvcnRfbGFiZWwpLCBzaXplID0gMykgKwogIGdlb21fcG9pbnQoZGF0YSA9IGR1YWxfZGMuaGlnaCAlPiUgZmlsdGVyKHJvd19udW1iZXIoKSAlJSAxMDAgPT0wKSwgYWVzKHggPSB0b3RhbCwgeSA9IFIsIGNvbG9yID0gbGFiZWxfYWx0LCBzaGFwZSA9IGxhYmVsX2FsdCksIHNpemUgPSAyKSArCiAgZ2VvbV9wYXRoKGRhdGEgPSB0aW1lX2hpZ2gudmlyLCBhZXMoeD0gdG90YWwsIHkgPSBSLCBncm91cCA9IHNob3J0X2xhYmVsLCBjb2xvciA9IHNob3J0X2xhYmVsKSwgc2l6ZSA9IDEsIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSkgKwogICBnZW9tX3BhdGgoZGF0YSA9IGR1YWxfZGMuaGlnaCwgYWVzKHg9IHRvdGFsLCB5ID0gUiwgZ3JvdXAgPSBsYWJlbF9hbHQsIGNvbG9yID0gbGFiZWxfYWx0KSwgc2l6ZSA9IDEsIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBhbmdsZSA9IDEwLCBsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YyggIiM0NTc1YjQiLCAiI2ZkY2I0NCIsICIjOTFiZmRiIiwgIiNmYzhkNTkiLCAiYmxhY2siKSkgICsKICAgIGxhYnMoZmlsbCA9ICJDdWVzIiwgeCA9ICJUb3RhbCBpUkJDIiwgeSA9ICJSQkMiLCBjb2xvciA9ICJDdWVzIikgKwogIHRoZW1lX2NsYXNzaWMoKSsgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGZ1bmN0aW9uKHgpIGZvcm1hdCh4LCBzY2llbnRpZmljID0gVFJVRSwgYWNjdXJhY3kgPSAwLjEpKSArCiAgZ3VpZGVzKHNoYXBlID0gRkFMU0UpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikKYGBgCgojLS0tLS0tLS0tYXJlYSBvZiBjdXJ2ZSB2cyBmaXRuZXNzLS0tLS0tLS0tLS0tLS0tIwojIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBhcmVhIHdpdGhpbiBjdXJ2ZQpgYGB7cn0KZ2V0X2RjX2FyZWEgPC0gZnVuY3Rpb24oZGYpewogIHggPC0gZGYkdG90YWwKICB5IDwtIGRmJFIKICBjaHVsbCh4LHkpLT5pCiAgcmV0dXJuKGFyZWFwbChjYmluZCh4W2ldLHlbaV0pKSkKfQpgYGAKCiMgc2luZ2xlIGN1ZSBhcmVhCmBgYHtyfQojIHNwbGl0IGRmCnNpX2RjLmxzIDwtIHNwbGl0KHNpX2RjLmRmLCBzaV9kYy5kZiRpZCkKCiMgZ2V0IGFyZWEKc2lfZGMuYXJlYSA8LSBjYmluZC5kYXRhLmZyYW1lKGFyZWEgPSBhcy5udW1lcmljKGxhcHBseShzaV9kYy5scywgZ2V0X2RjX2FyZWEpKSwgaWQgPSBuYW1lcyhsYXBwbHkoc2lfZGMubHMsIGdldF9kY19hcmVhKSkpCgojIGpvaW4gd2l0aCBmaXRuZXNzCnNpX29wdF9hcmVhLmRmIDwtIHNpX29wdC5kZiAlPiUgCiAgZmlsdGVyKGN1ZSAhPSAidCIpICU+JSAKICBsZWZ0X2pvaW4oc2lfZGMuYXJlYSwgYnkgPSAiaWQiKSAlPiUgCiAgbXV0YXRlKGZpdG5lc3Nfbm9ybSA9IGZpdG5lc3NfMjAvOS44ODM2MDIpICMjIG5vcm1hbGl6ZSBmaXRuZXNzICh1c2UgZml0bmVzcyBvYnRhaW5lZCB1c2luZyB0aW1lIGRmPTkgZ2l2ZW4gdGhhdCB3ZSBhcmUgcGxvdHRpbmcgZHVhbCBjdWUgYW5kIHNpbmdsZSBvbiB0aGUgc2FtZSBncmFwaCEpCgojIHNhbml0eSBjaGVjayBhcmVhLiBUaGUgYXJlYSBzaG91bGQgYmUgb24gdGhlIG9yZGVyIGlmIHdlIGFzc3VtZSBhIHJlY3RhbmdsZSEKc2lfZGMuZGYgJT4lIAogIGdyb3VwX2J5KGlkKSAlPiUgCiAgc3VtbWFyaXNlKFJfZGlmZiA9IG1heChSKS1taW4oUiksCiAgICAgICAgICAgIHRvdGFsX2RpZmYgPSBtYXgodG90YWwpLW1pbih0b3RhbCksCiAgICAgICAgICAgIHByb2R1Y3QgPSBSX2RpZmYqdG90YWxfZGlmZikKYGBgCgojIGR1YWwgY3VlCmBgYHtyfQojIHNwbGl0CmR1YWxfZGMubHMgPC0gc3BsaXQoZHVhbF9kYy5kZiwgZHVhbF9kYy5kZiRsYWJlbF9hbHQpCgojIGdldCBhcmVhCmR1YWxfZGMuYXJlYSA8LSBjYmluZC5kYXRhLmZyYW1lKGFyZWEgPSBhcy5udW1lcmljKGxhcHBseShkdWFsX2RjLmxzLCBnZXRfZGNfYXJlYSkpLCBsYWJlbF9hbHQgPSBuYW1lcyhsYXBwbHkoZHVhbF9kYy5scywgZ2V0X2RjX2FyZWEpKSkKCiMgam9pbiB3aXRoIGZpdG5lc3MKZHVhbF9jdWVfZl9maW5hbF9hcmVhLmRmIDwtIGR1YWxfY3VlX2ZfZmluYWwuZGYgJT4lIAogIG11dGF0ZShsYWJlbF9hbHQgPSBnc3ViKCJJXFwrSWciLCAiVG90YWwgSSIsIHBhc3RlKGxhYmVsLCAiJiIgLCBsYWJlbF9iKSkpICU+JSAKICBsZWZ0X2pvaW4oZHVhbF9kYy5hcmVhLCBieSA9IGMoImxhYmVsX2FsdCIpKSAlPiUgCiAgbXV0YXRlKGZpdG5lc3Nfbm9ybSA9IGZpdG5lc3MvOS44ODM2MDIpICMjIG5vcm1hbGl6ZSBmaXRuZXNzICh1c2UgZml0bmVzcyBvYnRhaW5lZCB1c2luZyB0aW1lIGRmPTkgZ2l2ZW4gdGhhdCB3ZSBhcmUgcGxvdHRpbmcgZHVhbCBjdWUgYW5kIHNpbmdsZSBvbiB0aGUgc2FtZSBncmFwaCEpCmBgYAoKIyBnZXQgYXJlYSBmb3IgaWRlYWwgYW5kIHN0YXRpYyBjcgpgYGB7cn0KIyB0aW1lCnRpbWVfaGlnaC5hcmVhIDwtIGdldF9kY19hcmVhKHRpbWVfaGlnaC52aXIpCgpgYGAKCiMgZml0IEdMTQpOb3RlIHRoYXQgc3RhdGljIGFuZCB0aW1lIGJhc2VkIGN1ZSBpcyBub3QgaW5jbHVkZWQgaW4gdGhlIG1vZGVsIQpgYGB7cn0KIyBmaXQgbG0Kc2lfbG0gPC0gbG0oZml0bmVzc19ub3JtIH4gYXJlYSwgZGF0YSA9IHNpX29wdF9hcmVhLmRmKQpkdWFsX2xtIDwtIGxtKGZpdG5lc3Nfbm9ybSB+IGFyZWEsIGRhdGEgPSBkdWFsX2N1ZV9mX2ZpbmFsX2FyZWEuZGYpCgojIGdldCBwcmVkaWN0aW9uIHdpdGggOTUlIENJCnNpX2xtLmNpID0gcHJlZGljdChzaV9sbSwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpICU+JSBjYmluZC5kYXRhLmZyYW1lKGFyZWEgPSBzaV9vcHRfYXJlYS5kZiRhcmVhKQpkdWFsX2xtLmNpID0gcHJlZGljdChkdWFsX2xtLCBpbnRlcnZhbCA9ICJjb25maWRlbmNlIikgJT4lIGNiaW5kLmRhdGEuZnJhbWUoYXJlYSA9IGR1YWxfY3VlX2ZfZmluYWxfYXJlYS5kZiRhcmVhKQoKIyBsb29rIGF0IFJeMgpzdW1tYXJ5KHNpX2xtKSRyLnNxdWFyZWQgIyMgMC4xNzY1MjQ4CnN1bW1hcnkoZHVhbF9sbSkkci5zcXVhcmVkICMjIDAuMTA1MzU5MQpgYGAKCiMgcGxvdCBjb3JyZWxhdGlvbgpgYGB7cn0KIyBzaW5nbGUgY3VlCnNpX2NvcnJfYXJlYS5wbHQgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoZGF0YSA9IHNpX29wdF9hcmVhLmRmLCBhZXMoeCA9IGFyZWEsIHkgPSBmaXRuZXNzX25vcm0sIGNvbG9yID0gIlNpbmdsZSBjdWUiLCBzaGFwZSA9ICJTaW5nbGUgY3VlIiksIHNpemUgPSAzLCBhbHBoYSA9IDAuNykgKwogIGdlb21fcG9pbnQoYWVzKHggPSB0aW1lX2hpZ2guYXJlYSwgeSA9IG1heCh0aW1lX2hpZ2guZHluICU+JSBmaWx0ZXIodmFyaWFibGUgPT0gInRhdV9jdW0iICYgdGltZSA9PSAyMCkgJT4lIHB1bGwodmFsdWUpKS85Ljg4MzYwMiwgY29sb3IgPSAiVGltZSIsIHNoYXBlID0gIlRpbWUiKSwgc2l6ZSA9IDMpICsKICBnZW9tX2xpbmUoZGF0YSA9IHNpX2xtLmNpLCBhZXMoeCA9IGFyZWEsIGZpdCkpICsKICBnZW9tX3JpYmJvbihkYXRhID0gc2lfbG0uY2ksIGFlcyh4ID0gYXJlYSwgeW1pbiA9IGx3ciwgeW1heCA9IHVwciksIGFscGhhID0gLjE1KSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSAiUl4yPTAuMTgiLCB4ID0gMS42KigxMF4xMSksIHkgPSAwLjc2KSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgbGFiZWwgPSAiVGltZSIsIHggPSAyLjcqKDEwXjExKSwgeSA9IDAuOTgpICsKICBsYWJzKHggPSAiQXJlYSIsIHkgPSAiTm9ybWFsaXplZCBmaXRuZXNzIiwgc2hhcGUgPSAiQ3VlIGNhdGVnb3J5IiwgY29sb3IgPSAiQ3VlIGNhdGVnb3J5IikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKG9yYW5nZSwiYmxhY2siKSkgKwogIHhsaW0obWluKHNpX29wdF9hcmVhLmRmJGFyZWEpLCBtYXgoZHVhbF9jdWVfZl9maW5hbF9hcmVhLmRmJGFyZWEpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgojIGR1YWwgY3VlCmR1YWxfY29ycl9hcmVhLnBsdCA8LWdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkdWFsX2N1ZV9mX2ZpbmFsX2FyZWEuZGYsIGFlcyh4ID0gYXJlYSwgeSA9IGZpdG5lc3Nfbm9ybSwgY29sb3IgPSAiRHVhbCBjdWUiLCBzaGFwZSA9ICJEdWFsIGN1ZSIpLCBzaXplID0gMywgYWxwaGEgPSAwLjcpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdGltZV9oaWdoLmFyZWEsIHkgPSBtYXgodGltZV9oaWdoLmR5biAlPiUgZmlsdGVyKHZhcmlhYmxlID09ICJ0YXVfY3VtIiAmIHRpbWUgPT0gMjApICU+JSBwdWxsKHZhbHVlKSkvOS44ODM2MDIsIGNvbG9yID0gIlRpbWUiLCBzaGFwZSA9ICJUaW1lIiksIHNpemUgPSAzKSArCiAgZ2VvbV9saW5lKGRhdGEgPSBkdWFsX2xtLmNpLCBhZXMoeCA9IGFyZWEsIGZpdCkpICsKICBnZW9tX3JpYmJvbihkYXRhID0gZHVhbF9sbS5jaSwgYWVzKHggPSBhcmVhLCB5bWluID0gbHdyLCB5bWF4ID0gdXByKSwgYWxwaGEgPSAuMTUpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9ICJSXjI9MC4xMSIsIHggPSAxLjYqKDEwXjExKSwgeSA9IDAuNzYpICsKICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCBsYWJlbCA9ICJUaW1lIiwgeCA9IDIuNyooMTBeMTEpLCB5ID0gMC45OCkgKwogIGxhYnMoeCA9ICJBcmVhIiwgeSA9ICJOb3JtYWxpemVkIGZpdG5lc3MiLCBzaGFwZSA9ICJDdWUgY2F0ZWdvcnkiLCBjb2xvciA9ICJDdWUgY2F0ZWdvcnkiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYmx1ZSwgImJsYWNrIikpICsKICB4bGltKG1pbihzaV9vcHRfYXJlYS5kZiRhcmVhKSwgbWF4KGR1YWxfY3VlX2ZfZmluYWxfYXJlYS5kZiRhcmVhKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMtLS0tLS0tLS1hc3NlbWJsZSBmaWd1cmUtLS0tLS0tLS0tLS0jCmBgYHtyfQpzaV9kY19hcmVhLnBsdCA8LSBnZ2FycmFuZ2Uoc2lfZGMucGx0LCBzaV9jb3JyX2FyZWEucGx0LCBuY29sID0gMiwgbnJvdyA9IDEsIGFsaWduID0gImh2IiwgY29tbW9uLmxlZ2VuZCA9IFQsIGxhYmVscyA9IGMoIkEiLCAiQiIpKQpkdWFsX2RjX2FyZWEucGx0IDwtIGdnYXJyYW5nZShkdWFsX2RjLnBsdCwgZHVhbF9jb3JyX2FyZWEucGx0LCBuY29sID0gMiwgbnJvdyA9IDEsIGFsaWduID0gImh2IiwgY29tbW9uLmxlZ2VuZCA9IFQsIGxhYmVscyA9IGMoIkMiLCAiRCIpKQoKZ2dhcnJhbmdlKHNpX2RjX2FyZWEucGx0LCBkdWFsX2RjX2FyZWEucGx0LCBucm93ID0gMiwgYWxpZ24gPSAidiIpCgpnZ3NhdmUodW5pdHMgPSAicHgiLCBkcGkgPSAzMDAsIHdpZHRoID0gMjU1MCwgaGVpZ2h0ID0gMjAwMCwgZmlsZW5hbWUgPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy92aXJ1bGVuY2UudGlmZiIpLCBiZyA9ICJ3aGl0ZSIsIHNjYWxlID0gMSkKYGBgCgoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09IwojIFZhbGlkYXRpb24gb2Ygb3B0aW1pemF0aW9uCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PSMKIy0tLS0tLSBwcm9jZXNzIGlucHV0IGRhdGEgLS0tLS0tIwojIFByb2Nlc3MgZGF0YQpgYGB7cn0KIyMgZ2V0IGRpZmZlcmVuY2UgYmV0d2VlbnQgZml0bmVzcyBjb25mZXJyZWQgYnkgbW9kZWwgcHJvZHVjZWQgYnkgYSByYW5kb20gc3BsaW5lIHN0cmF0ZWd5ICJWMSIgdnMgb3B0aW1pemVkIGZpdG5lc3MKdmFsaWRhdGlvbi5kZl9wIDwtIHZhbGlkYXRpb24uZGYgJT4lIAogIGxlZnRfam9pbihzZWxlY3Qoc2lfb3B0LmRmLCBpZCwgZml0bmVzc18yMCwgc2hvcnRfbGFiZWwsIGxvbmdfbGFiZWwpLCBieSA9IGMoImlkIikpICU+JSAKICBtdXRhdGUoZGlmZiA9IGZpdG5lc3NfMjAtVjEsCiAgICAgICAgIGxvbmdfc2hvcnRfbGFiZWwgPSBnc3ViKCJJXFwrSWciLCAiVG90YWwgSSIscGFzdGUwKGxvbmdfbGFiZWwsICIgKCIsIHNob3J0X2xhYmVsLCAiKSIpKSkKCiMjIG5vIHJhbmRvbSBzdHJhdGVneSBwZXJmb3JtZWQgYmV0dGVyIQp2YWxpZGF0aW9uLmRmX3AgJT4lIGZpbHRlcihkaWZmPD0wKQpgYGAKCiMgUGxvdApgYGB7cn0KZ2dwbG90KHZhbGlkYXRpb24uZGZfcCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGRpZmYpLCBmaWxsID0gImxpZ2h0IGdyZXkiKSArCiAgZmFjZXRfd3JhcCh+bG9uZ19zaG9ydF9sYWJlbCwgc2NhbGVzID0gImZyZWUiKSArCiAgbGFicyh4ID0gIkZpdG5lc3MgZGlmZmVyZW5jZSAoT3B0aW1hbC1SYW5kb20pIiwgeSA9ICJEZW5zaXR5IikgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgCgpnZ3NhdmUodW5pdHMgPSAicHgiLCBkcGkgPSAzMDAsIHdpZHRoID0gMjU1MCwgaGVpZ2h0ID0gMTUwMCwgZmlsZW5hbWUgPSBoZXJlKCJjb2RlX3JlcG9zaXRvcnkvZmlndXJlcy9zaV92YWxpZGF0aW9uLnRpZmYiKSwgYmcgPSAid2hpdGUiLCBzY2FsZSA9IDEpCmBgYAoKCgoKCg==